Merge branch 'v1.6.0'

This commit is contained in:
2020-08-08 20:03:45 -04:00
96 changed files with 6371 additions and 2050 deletions
+2427 -914
View File
File diff suppressed because it is too large Load Diff
+48 -50
View File
@@ -2,55 +2,7 @@
"name": "watch-party-crx",
"version": "1.6.0",
"private": true,
"dependencies": {
"@giphy/js-fetch-api": "^1.5.8",
"@giphy/react-components": "^1.0.4",
"@material-ui/core": "^4.9.10",
"@material-ui/icons": "^4.9.1",
"@voxeet/voxeet-web-sdk": "^2.3.1",
"autoprefixer": "7.1.6",
"babel-core": "6.26.3",
"babel-eslint": "10.x",
"babel-jest": "20.0.3",
"babel-preset-react-app": "^9.0.1",
"babel-runtime": "6.26.0",
"case-sensitive-paths-webpack-plugin": "2.1.1",
"chalk": "1.1.3",
"css-loader": "0.28.7",
"dotenv": "4.0.0",
"dotenv-expand": "4.2.0",
"eslint": "6.x",
"eslint-config-react-app": "^5.0.1",
"eslint-loader": "2.2.1",
"eslint-plugin-flowtype": "3.x",
"eslint-plugin-import": "2.x",
"eslint-plugin-jsx-a11y": "6.x",
"eslint-plugin-react": "7.x",
"file-loader": "4.2.0",
"firebase": "^7.14.0",
"fs-extra": "3.0.1",
"html-webpack-plugin": "^4.0.0-beta.8",
"jest": "20.0.4",
"object-assign": "4.1.1",
"postcss-flexbugs-fixes": "3.2.0",
"postcss-loader": "2.0.8",
"promise": "8.0.1",
"prop-types": "^15.7.2",
"raf": "3.4.0",
"react": "^16.4.1",
"react-chat-ui": "^0.3.2",
"react-dev-utils": "^9.0.0-next.68",
"react-dom": "^16.4.1",
"react-frame-component": "^4.0.0",
"resolve": "1.6.0",
"route-lite": "^0.3.0",
"style-loader": "0.19.0",
"sw-precache-webpack-plugin": "^0.11.5",
"url-loader": "0.6.2",
"webpack-dev-server": "^3.11.0",
"webpack-manifest-plugin": "2.0.4",
"whatwg-fetch": "2.0.3"
},
"dependencies": {},
"scripts": {
"start": "node scripts/start.js",
"build": "node scripts/build.js",
@@ -101,6 +53,39 @@
"extends": "react-app"
},
"devDependencies": {
"@material-ui/core": "^4.9.10",
"@material-ui/icons": "^4.9.1",
"@material-ui/lab": "^4.0.0-alpha.56",
"@voxeet/voxeet-web-sdk": "^2.3.1",
"autoprefixer": "7.1.6",
"babel-runtime": "6.26.0",
"case-sensitive-paths-webpack-plugin": "2.1.1",
"chalk": "1.1.3",
"css-loader": "0.28.7",
"dotenv": "4.0.0",
"dotenv-expand": "4.2.0",
"file-loader": "4.2.0",
"firebase": "^7.14.0",
"fs-extra": "3.0.1",
"html-webpack-plugin": "^4.0.0-beta.8",
"jest": "20.0.4",
"object-assign": "4.1.1",
"postcss-flexbugs-fixes": "3.2.0",
"postcss-loader": "2.0.8",
"promise": "8.0.1",
"prop-types": "^15.7.2",
"raf": "3.4.0",
"react": "^16.4.1",
"react-dom": "^16.4.1",
"react-frame-component": "^4.0.0",
"react-giphy-searchbox": "^1.4.0",
"resolve": "1.6.0",
"route-lite": "^0.3.0",
"style-loader": "0.19.0",
"sw-precache-webpack-plugin": "^0.11.5",
"url-loader": "0.6.2",
"webpack-manifest-plugin": "2.0.4",
"whatwg-fetch": "2.0.3",
"@babel/core": "^7.1.6",
"@babel/preset-env": "^7.1.6",
"@babel/preset-react": "^7.0.0",
@@ -110,6 +95,19 @@
"eslint-plugin-react-hooks": "^2.0.1",
"mini-css-extract-plugin": "^0.8.0",
"webpack": "^4.42.1",
"webpack-cli": "^3.3.11"
"webpack-cli": "^3.3.11",
"babel-core": "6.26.3",
"babel-eslint": "10.x",
"babel-jest": "20.0.3",
"babel-preset-react-app": "^9.0.1",
"eslint": "6.x",
"eslint-config-react-app": "^5.0.1",
"eslint-loader": "2.2.1",
"eslint-plugin-flowtype": "3.x",
"eslint-plugin-import": "2.x",
"eslint-plugin-jsx-a11y": "6.x",
"eslint-plugin-react": "7.x",
"webpack-dev-server": "^3.11.0",
"react-dev-utils": "^9.0.0-next.68"
}
}
+1 -8
View File
@@ -5,16 +5,9 @@ Proprietary and confidential
Written by Ayushya Amitabh <admin@chromewatchparty.com>, 2020
https://www.chromewatchparty.com/copyright
=========================================================================
ALL CONTENT ON IN WATCH PARTY'S CHROME EXTENSION APP, UNLESS OTHERWISE
ALL CONTENT ON WATCH PARTY'S WEBSITE OR IN WATCH PARTY'S CHROME EXTENSION APP, UNLESS OTHERWISE
CITED ARE THE SOLE INTELECTUAL PROPERTY OF AYUSHYA AMITABH, AND MAY NOT BE
COPIED, EDITED, OR REPRODUCED WITHOUT PERMISSION.
Netflix logo is a trademark and intellectual property of Netflix, Inc.
Disney+ logo is a trademark and intellectual property of The Walt Disney Company
Prime logo is a trademark and intellectual property of Amazon.com, Inc.
YouTube logo is a trademark and intellectual property of Google, Inc.
Hulu logo is a trademark and intellectual property of Hulu, LLC.
HBO Now and HBO Max logos are trademark and intellectual property of WarnerMedia Direct,LLC.
------------------------------------------------------------------------->
<!DOCTYPE html>
<html>
+69 -14
View File
@@ -5,20 +5,16 @@ Proprietary and confidential
Written by Ayushya Amitabh <admin@chromewatchparty.com>, 2020
https://www.chromewatchparty.com/copyright
=========================================================================
ALL CONTENT ON IN WATCH PARTY'S CHROME EXTENSION APP, UNLESS OTHERWISE
ALL CONTENT ON WATCH PARTY'S WEBSITE OR IN WATCH PARTY'S CHROME EXTENSION APP, UNLESS OTHERWISE
CITED ARE THE SOLE INTELECTUAL PROPERTY OF AYUSHYA AMITABH, AND MAY NOT BE
COPIED, EDITED, OR REPRODUCED WITHOUT PERMISSION.
Netflix logo is a trademark and intellectual property of Netflix, Inc.
Disney+ logo is a trademark and intellectual property of The Walt Disney Company
Prime logo is a trademark and intellectual property of Amazon.com, Inc.
YouTube logo is a trademark and intellectual property of Google, Inc.
Hulu logo is a trademark and intellectual property of Hulu, LLC.
HBO Now and HBO Max logos are trademark and intellectual property of WarnerMedia Direct,LLC.
************************************************************************/
/* global chrome */
/* global firebase */
const APP_VERSION = '1.6.0';
const APP_VERSION_STRING = APP_VERSION.replace(/\./g, '');
const firebaseConfig = {
apiKey: 'AIzaSyCJLuYpzXLDRmu2ttzFuSGmYcpFxqToWFU',
authDomain: 'watch-party-crx.firebaseapp.com',
@@ -68,11 +64,40 @@ const sendAuth = async (sendResponse) => {
sendResponse(firebase.auth().currentUser);
};
chrome.runtime.onInstalled.addListener(() => {
chrome.tabs.create({
url: 'https://www.chromewatchparty.com/welcome',
active: true,
});
chrome.runtime.onInstalled.addListener((details) => {
if (details.reason) {
if (details.reason === 'install') {
if (window.localStorage.getItem('watchPartyVersion')) {
if (window.localStorage.getItem('watchPartyVersion') !== APP_VERSION) {
chrome.tabs.create({
url: `http://www.chromewatchparty.com/changes/v${APP_VERSION}`,
active: true,
});
}
} else {
chrome.tabs.create({
url: 'https://www.chromewatchparty.com/welcome',
active: true,
});
window.localStorage.setItem('watchPartyVersion', APP_VERSION);
}
} else if (details.reason === 'update') {
if (window.localStorage.getItem('watchPartyVersion')) {
if (window.localStorage.getItem('watchPartyVersion') !== APP_VERSION) {
chrome.tabs.create({
url: `http://www.chromewatchparty.com/changes/v${APP_VERSION}`,
active: true,
});
}
} else {
chrome.tabs.create({
url: `http://www.chromewatchparty.com/changes/v${APP_VERSION}`,
active: true,
});
window.localStorage.setItem('watchPartyVersion', APP_VERSION);
}
}
}
});
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
@@ -84,6 +109,16 @@ chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
case 'GET_AUTH':
sendAuth(sendResponse);
break;
case 'GET_PARTIES':
sendResponse(
window.localStorage.getItem('parties')
? JSON.parse(window.localStorage.getItem('parties'))
: []
);
break;
case 'EMPTY_PARTIES':
window.localStorage.removeItem('parties');
break;
default:
console.warn('Unknown message type: <', type, '>');
break;
@@ -98,7 +133,7 @@ chrome.runtime.onConnect.addListener((port) => {
port.messageListener && port.messageListener();
});
port.onMessage.addListener((request) => {
const { type, roomId } = request;
const { type, roomId, roomPin } = request;
if (type === 'INIT') {
port.roomListener = firebase
.firestore()
@@ -152,6 +187,26 @@ chrome.runtime.onConnect.addListener((port) => {
port.postMessage({ isError: true, ...err });
}
);
if (window.localStorage.getItem('parties')) {
var existing = JSON.parse(window.localStorage.getItem('parties'));
if (!(existing.filter((i) => i.name === roomId).length > 0)) {
existing.push({
name: roomId,
pin: roomPin,
});
window.localStorage.setItem('parties', JSON.stringify(existing));
}
} else {
window.localStorage.setItem(
'parties',
JSON.stringify([
{
name: roomId,
pin: roomPin,
},
])
);
}
}
});
});
+1 -8
View File
@@ -5,16 +5,9 @@ Proprietary and confidential
Written by Ayushya Amitabh <admin@chromewatchparty.com>, 2020
https://www.chromewatchparty.com/copyright
=========================================================================
ALL CONTENT ON IN WATCH PARTY'S CHROME EXTENSION APP, UNLESS OTHERWISE
ALL CONTENT ON WATCH PARTY'S WEBSITE OR IN WATCH PARTY'S CHROME EXTENSION APP, UNLESS OTHERWISE
CITED ARE THE SOLE INTELECTUAL PROPERTY OF AYUSHYA AMITABH, AND MAY NOT BE
COPIED, EDITED, OR REPRODUCED WITHOUT PERMISSION.
Netflix logo is a trademark and intellectual property of Netflix, Inc.
Disney+ logo is a trademark and intellectual property of The Walt Disney Company
Prime logo is a trademark and intellectual property of Amazon.com, Inc.
YouTube logo is a trademark and intellectual property of Google, Inc.
Hulu logo is a trademark and intellectual property of Hulu, LLC.
HBO Now and HBO Max logos are trademark and intellectual property of WarnerMedia Direct,LLC.
------------------------------------------------------------------------->
<!DOCTYPE html>
<html>
+6 -2
View File
@@ -17,7 +17,9 @@
"https://*.youtube.com/*",
"https://play.hbonow.com/*",
"https://play.hbomax.com/*",
"https://*.peacocktv.com/*"
"https://*.peacocktv.com/*",
"https://play.google.com/movies/*",
"https://*.crunchyroll.com/*"
],
"content_security_policy": "script-src 'self' https://www.gstatic.com/ https://*.firebaseio.com https://www.googleapis.com; object-src 'self'",
"content_scripts": [
@@ -30,7 +32,9 @@
"https://*.youtube.com/*",
"https://play.hbonow.com/*",
"https://play.hbomax.com/*",
"https://*.peacocktv.com/*"
"https://*.peacocktv.com/*",
"https://play.google.com/*",
"https://*.crunchyroll.com/*"
],
"run_at": "document_idle",
"js": ["/static/js/content.js"],
+24 -9
View File
@@ -5,20 +5,17 @@ Proprietary and confidential
Written by Ayushya Amitabh <admin@chromewatchparty.com>, 2020
https://www.chromewatchparty.com/copyright
=========================================================================
ALL CONTENT ON IN WATCH PARTY'S CHROME EXTENSION APP, UNLESS OTHERWISE
ALL CONTENT ON WATCH PARTY'S WEBSITE OR IN WATCH PARTY'S CHROME EXTENSION APP, UNLESS OTHERWISE
CITED ARE THE SOLE INTELECTUAL PROPERTY OF AYUSHYA AMITABH, AND MAY NOT BE
COPIED, EDITED, OR REPRODUCED WITHOUT PERMISSION.
Netflix logo is a trademark and intellectual property of Netflix, Inc.
Disney+ logo is a trademark and intellectual property of The Walt Disney Company
Prime logo is a trademark and intellectual property of Amazon.com, Inc.
YouTube logo is a trademark and intellectual property of Google, Inc.
Hulu logo is a trademark and intellectual property of Hulu, LLC.
HBO Now and HBO Max logos are trademark and intellectual property of WarnerMedia Direct,LLC.
------------------------------------------------------------------------->
<!DOCTYPE html>
<html>
<head>
<meta
http-equiv="Content-Security-Policy"
content="default-src *; style-src 'self' https://* 'unsafe-inline'; script-src 'self' http://* 'unsafe-inline' 'unsafe-eval'"
/>
<style>
a {
display: table;
@@ -34,6 +31,7 @@ HBO Now and HBO Max logos are trademark and intellectual property of WarnerMedia
cursor: pointer;
border-radius: 36px;
text-decoration: none;
outline: none;
}
p {
word-break: break-word;
@@ -50,15 +48,30 @@ HBO Now and HBO Max logos are trademark and intellectual property of WarnerMedia
background-color: #c0f300;
color: black;
}
body {
min-width: max-content;
padding: 5px 10px;
background: #424242;
}
button {
padding: 7.5px 3px;
margin: 10px 0;
border-radius: 13px;
width: 100%;
background: #c0f300;
outline: none !important;
border: none;
cursor: pointer;
filter: drop-shadow(0px 0px 15px rgba(0, 0, 0, 0.7));
transition: all ease-in 0.3s;
}
button:hover {
filter: drop-shadow(0px 0px 3px rgba(0, 0, 0, 0.7)) !important;
}
</style>
</head>
<body>
<script src="popup.js"></script>
<p>
NOTHING TO DO HERE. GO TO A SUPPORTED WEBSITE, AND LOOK FOR WATCH PARTY IN THE BOTTOM RIGHT
CORNER OF YOUR SCREEN
@@ -67,5 +80,7 @@ HBO Now and HBO Max logos are trademark and intellectual property of WarnerMedia
<a target="_blank" href="https://www.chromewatchparty.com/welcome/#tips">FAQ</a>
<a target="_blank" href="mailto:admin@chromewatchparty.com?subject=User Feedback">Feedback</a>
<a target="_blank" href="https://paypal.me/Ayushya">Donate</a>
<p style="width: 100%; max-width: 100%;"></p>
<button id="clear-button">Clear Party Names & Pins</button>
</body>
</html>
+18
View File
@@ -0,0 +1,18 @@
/************************************************************************
Copyright (C) Ayushya Amitabh - All Rights Reserved
Unauthorized copying of this file, via any medium is strictly prohibited
Proprietary and confidential
Written by Ayushya Amitabh <admin@chromewatchparty.com>, 2020
https://www.chromewatchparty.com/copyright
=========================================================================
ALL CONTENT ON WATCH PARTY'S WEBSITE OR IN WATCH PARTY'S CHROME EXTENSION APP, UNLESS OTHERWISE
CITED ARE THE SOLE INTELECTUAL PROPERTY OF AYUSHYA AMITABH, AND MAY NOT BE
COPIED, EDITED, OR REPRODUCED WITHOUT PERMISSION.
************************************************************************/
function clearLocal() {
window.localStorage.removeItem('parties');
return 0;
}
window.onload = function () {
document.getElementById('clear-button').onclick = clearLocal;
};
+39 -11
View File
@@ -5,20 +5,13 @@ Proprietary and confidential
Written by Ayushya Amitabh <admin@chromewatchparty.com>, 2020
https://www.chromewatchparty.com/copyright
=========================================================================
ALL CONTENT ON IN WATCH PARTY'S CHROME EXTENSION APP, UNLESS OTHERWISE
ALL CONTENT ON WATCH PARTY'S WEBSITE OR IN WATCH PARTY'S CHROME EXTENSION APP, UNLESS OTHERWISE
CITED ARE THE SOLE INTELECTUAL PROPERTY OF AYUSHYA AMITABH, AND MAY NOT BE
COPIED, EDITED, OR REPRODUCED WITHOUT PERMISSION.
Netflix logo is a trademark and intellectual property of Netflix, Inc.
Disney+ logo is a trademark and intellectual property of The Walt Disney Company
Prime logo is a trademark and intellectual property of Amazon.com, Inc.
YouTube logo is a trademark and intellectual property of Google, Inc.
Hulu logo is a trademark and intellectual property of Hulu, LLC.
HBO Now and HBO Max logos are trademark and intellectual property of WarnerMedia Direct,LLC.
************************************************************************/
/*global chrome*/
const API_HOST = 'https://us-central1-watch-party-crx.cloudfunctions.net/widgets2';
const API_HOST = 'https://us-central1-watch-party-crx.cloudfunctions.net/widgets';
export const PLAYER_STATUS = {
PLAYING: 'PLAYING',
@@ -35,6 +28,8 @@ export const PLAYER_SOURCE = {
HBONOW: 'hbonow.',
HBOMAX: 'hbomax.',
PEACOCK: 'peacocktv.',
GOOGLEPLAY: 'play.google.',
CRUNCHYROLL: 'crunchyroll.',
LOCAL: 'localhost',
};
@@ -48,6 +43,8 @@ export const getPlayerSource = () => {
if (HREF.includes(PLAYER_SOURCE.HBONOW)) return PLAYER_SOURCE.HBONOW;
if (HREF.includes(PLAYER_SOURCE.HBOMAX)) return PLAYER_SOURCE.HBOMAX;
if (HREF.includes(PLAYER_SOURCE.PEACOCK)) return PLAYER_SOURCE.PEACOCK;
if (HREF.includes(PLAYER_SOURCE.GOOGLEPLAY)) return PLAYER_SOURCE.GOOGLEPLAY;
if (HREF.includes(PLAYER_SOURCE.CRUNCHYROLL)) return PLAYER_SOURCE.CRUNCHYROLL;
if (HREF.includes(PLAYER_SOURCE.LOCAL)) return PLAYER_SOURCE.LOCAL;
};
@@ -71,6 +68,13 @@ export const emptyLocalData = () => {
window.localStorage.removeItem('WatchParty');
};
export const scrollThread = () => {
setTimeout(function () {
const pcc = document.querySelector('.thread-wrapper');
if (pcc) pcc.scrollTop = pcc.scrollHeight;
}, 500);
};
export const getUserUid = async () =>
new Promise((resolve, reject) => {
chrome.runtime.sendMessage(
@@ -103,6 +107,23 @@ export const fetcher = async ({ endpoint, method, ...args }) => {
);
};
export const webhookFetcher = async ({ endpoint, ...args }) => {
return new Promise((resolve, reject) =>
chrome.runtime.sendMessage(
{
type: 'API_CALL',
url: endpoint,
method: 'POST',
...args,
},
(response) => {
if (response.status > 299) reject(response);
else resolve({ json: () => response });
}
)
);
};
export const CreateRoomApi = async (data) => {
return fetcher({
endpoint: 'create',
@@ -149,9 +170,9 @@ export const UpdatePlayerApi = async (data) => {
});
};
export const GetVoxeetTokenApi = async () => {
export const GetVoxeetTokenApi = async (data) => {
return fetcher({
endpoint: 'validator',
endpoint: `validator/${data.partyName}`,
method: 'GET',
});
};
@@ -170,3 +191,10 @@ export const UpdateVoxeetApi = async (data) => {
body: JSON.stringify(data),
});
};
export const UpdateAdminOnly = async (data) => {
return fetcher({
endpoint: `updateAdminOnly/${data.partyName}/${data.adminOnly}`,
method: 'PUT',
});
};
+37 -10
View File
@@ -5,20 +5,13 @@ Proprietary and confidential
Written by Ayushya Amitabh <admin@chromewatchparty.com>, 2020
https://www.chromewatchparty.com/copyright
=========================================================================
ALL CONTENT ON IN WATCH PARTY'S CHROME EXTENSION APP, UNLESS OTHERWISE
ALL CONTENT ON WATCH PARTY'S WEBSITE OR IN WATCH PARTY'S CHROME EXTENSION APP, UNLESS OTHERWISE
CITED ARE THE SOLE INTELECTUAL PROPERTY OF AYUSHYA AMITABH, AND MAY NOT BE
COPIED, EDITED, OR REPRODUCED WITHOUT PERMISSION.
Netflix logo is a trademark and intellectual property of Netflix, Inc.
Disney+ logo is a trademark and intellectual property of The Walt Disney Company
Prime logo is a trademark and intellectual property of Amazon.com, Inc.
YouTube logo is a trademark and intellectual property of Google, Inc.
Hulu logo is a trademark and intellectual property of Hulu, LLC.
HBO Now and HBO Max logos are trademark and intellectual property of WarnerMedia Direct,LLC.
************************************************************************/
import React, { useState, useEffect } from 'react';
import Router from 'route-lite';
import { Fab, createMuiTheme, ThemeProvider, Snackbar } from '@material-ui/core';
import { Fab, createMuiTheme, ThemeProvider, Snackbar, Tooltip } from '@material-ui/core';
import Create from './Create';
import Loading from './Loading';
import firebase from 'firebase/app';
@@ -47,6 +40,30 @@ const Routes = (props) => (
</Router>
);
const resetResize = () => {
const checkedX = 400;
document.documentElement.style.setProperty('--party-width', `${checkedX}px`);
document.documentElement.style.setProperty('--neg-party-width', `-${checkedX}px`);
};
const doResize = (ev) => {
const checkedX =
window.innerWidth -
Math.min(window.innerWidth - 300, Math.max(window.innerWidth * 0.5, ev.clientX));
document.documentElement.style.setProperty('--party-width', `${checkedX}px`);
document.documentElement.style.setProperty('--neg-party-width', `-${checkedX}px`);
};
const stopResize = (ev) => {
document.documentElement.removeEventListener('mousemove', doResize, false);
document.documentElement.removeEventListener('mouseup', stopResize, false);
};
const startResize = (ev) => {
document.documentElement.addEventListener('mousemove', doResize, false);
document.documentElement.addEventListener('mouseup', stopResize, false);
};
const App = () => {
const closedNotification = { open: false, message: '' };
@@ -114,6 +131,15 @@ const App = () => {
/>
<div className="party-content-wrapper">
<div id="party-routes-wrapper">
<Tooltip
placement="right-end"
title="Resize Sidebar / Double Click To Reset"
aria-label="Resize Sidebar / Double Click To Reset"
>
<div onDoubleClick={resetResize} onMouseDown={startResize} id="party-width-dragger">
||
</div>
</Tooltip>
{loading && <Loading />}
{status[APP_VERSION_STRING] && status[APP_VERSION_STRING].active === true ? (
<Routes
@@ -121,13 +147,14 @@ const App = () => {
notify={(message) => setNotification({ open: true, message })}
appVersion={APP_VERSION}
appVersionString={APP_VERSION_STRING}
status={status[APP_VERSION_STRING]}
/>
) : (
<Inactive
status={
status[APP_VERSION_STRING]
? status[APP_VERSION_STRING].message
: 'Uh oh, looks like our servers are down right now.'
: "Uh oh, can't connect to server. Trying again..."
}
/>
)}
+80 -51
View File
@@ -5,25 +5,18 @@ Proprietary and confidential
Written by Ayushya Amitabh <admin@chromewatchparty.com>, 2020
https://www.chromewatchparty.com/copyright
=========================================================================
ALL CONTENT ON IN WATCH PARTY'S CHROME EXTENSION APP, UNLESS OTHERWISE
ALL CONTENT ON WATCH PARTY'S WEBSITE OR IN WATCH PARTY'S CHROME EXTENSION APP, UNLESS OTHERWISE
CITED ARE THE SOLE INTELECTUAL PROPERTY OF AYUSHYA AMITABH, AND MAY NOT BE
COPIED, EDITED, OR REPRODUCED WITHOUT PERMISSION.
Netflix logo is a trademark and intellectual property of Netflix, Inc.
Disney+ logo is a trademark and intellectual property of The Walt Disney Company
Prime logo is a trademark and intellectual property of Amazon.com, Inc.
YouTube logo is a trademark and intellectual property of Google, Inc.
Hulu logo is a trademark and intellectual property of Hulu, LLC.
HBO Now and HBO Max logos are trademark and intellectual property of WarnerMedia Direct,LLC.
************************************************************************/
import VoxeetSDK from '@voxeet/voxeet-web-sdk';
import VoxeetSDK from "@voxeet/voxeet-web-sdk";
import {
GetVoxeetTokenApi,
GetRefreshedVoxeetTokenApi,
LocalData,
UpdateVoxeetApi,
updateLocalData,
} from './Api';
} from "./Api";
export default class CallManager {
constructor(listener, notify) {
@@ -33,12 +26,18 @@ export default class CallManager {
this.__accessToken = LocalData().vrT;
this.__sessionInitialized = false;
this.__admin = false;
this.__adminOnly = true;
}
toggleCall(onCall, conference, admin) {
this.listener({ callActionPending: true });
this.__admin = admin;
if (onCall === true) this.leaveCall();
else this.boot(conference);
toggleCall(onCall, conference, admin, adminOnly) {
try {
this.listener({ callActionPending: true });
this.__admin = admin;
this.__adminOnly = adminOnly;
if (onCall === true) this.leaveCall();
else this.boot(conference);
} catch (e) {
this.notify("Uh oh something went wrong, try rejoining the call.");
}
}
toggleMute(muted) {
this.listener({ callActionPending: true });
@@ -53,16 +52,23 @@ export default class CallManager {
.fetch(conference._id)
.then(this.joinCall)
.catch((err) => {
console.error('Failed to fetch conference', err);
console.error("Failed to fetch conference", err);
this.startCall();
});
} else if (opened && !conference) this.startCall();
else console.error('Unable to open Voxeet Session');
} else if (opened && !conference) {
this.startCall();
} else {
this.listener({ onCall: false, muted: false, callActionPending: false });
console.error("Unable to open Voxeet Session");
}
}
async startCall() {
if (this.__admin === true) {
if (!this.__adminOnly || (this.__adminOnly && this.__admin)) {
VoxeetSDK.conference
.create({ alias: LocalData().partyName, pin: LocalData().partyPin })
.create({
alias: `AUTODELETE_${LocalData().partyName}`,
pin: LocalData().partyPin,
})
.then(async (conference) => {
const updatedVoxeet = await UpdateVoxeetApi({
conference,
@@ -72,25 +78,55 @@ export default class CallManager {
})
.then((res) => res.json())
.then((res) => res.status <= 299)
.catch((err) => console.error('Update Voxeet API Error', err));
.catch((err) => {
if (err.conference) {
this.joinCall(err.conference);
return true;
} else {
this.notify(err.message);
this.listener({
onCall: false,
muted: false,
callActionPending: false,
});
console.error("Update Voxeet API Error", err);
return false;
}
});
if (updatedVoxeet === true) {
this.joinCall(conference);
} else {
console.error('Failed to update party Voxeet');
this.listener({
onCall: false,
muted: false,
callActionPending: false,
});
console.error("Failed to update party Voxeet");
}
});
} else {
this.notify('Ask your party admin to start the call.');
this.notify("Ask your party admin to start the call.");
this.leaveCall();
}
}
async joinCall(conference) {
const joined = await VoxeetSDK.conference
.join(conference, { constraints: { audio: true, video: false } })
.then(() => true);
if (!joined) {
this.startCall();
}
.then(() => true)
.catch((e) => {
this.listener({
onCall: false,
muted: false,
callActionPending: false,
});
this.notify("Unable to join call. Try again in a moment.");
console.error("Error joining call", e);
return true;
});
// Leave changes as comment for later
// if (!joined) {
// this.startCall();
// }
}
async leaveCall() {
VoxeetSDK.conference.leave().then(() => {
@@ -107,9 +143,9 @@ export default class CallManager {
});
return true;
} catch (error) {
console.error('Error Opening Voxeet Session', error);
console.error("Error Opening Voxeet Session", error);
}
console.error('Session not initialized');
console.error("Session not initialized");
return false;
}
return true;
@@ -122,33 +158,26 @@ export default class CallManager {
this.listener({ usersOnCall });
}
async initialize() {
VoxeetSDK.conference.on('participantAdded', () => this.usersListHandle());
VoxeetSDK.conference.on('participantUpdated', () => this.usersListHandle());
VoxeetSDK.conference.on("participantAdded", () => this.usersListHandle());
VoxeetSDK.conference.on("participantUpdated", () => this.usersListHandle());
VoxeetSDK.conference.on('joined', () => {
VoxeetSDK.conference.on("joined", () => {
this.listener({ onCall: true, callActionPending: false });
});
VoxeetSDK.conference.on('left', () => {
this.listener({ onCall: false, callActionPending: false, usersOnCall: {} });
VoxeetSDK.conference.on("left", () => {
this.listener({
onCall: false,
callActionPending: false,
usersOnCall: {},
});
});
VoxeetSDK.conference.on('streamAdded', (participant, stream) => {
if (participant !== VoxeetSDK.session.participant) {
const voiceNode = document.createElement('video');
voiceNode.srcObject = stream;
voiceNode.id = participant.id;
voiceNode.width = 0;
voiceNode.height = 0;
voiceNode.autoplay = true;
voiceNode.classList.add('voxeetAudioOut');
document.body.appendChild(voiceNode);
}
VoxeetSDK.conference.on("streamAdded", (participant, stream) => {
this.listener({ callActionPending: false });
});
VoxeetSDK.conference.on('streamRemoved', (participant, stream) => {
if (document.getElementById(participant.id)) document.getElementById(participant.id).remove();
VoxeetSDK.conference.on("streamRemoved", (participant, stream) => {
this.listener({ callActionPending: false });
});
@@ -158,7 +187,7 @@ export default class CallManager {
this.__accessToken !== null ||
this.__refreshToken !== null
) {
await GetVoxeetTokenApi()
await GetVoxeetTokenApi({ partyName: LocalData().partyName })
.then((res) => res.json())
.then((tokenResult) => {
if (tokenResult.status <= 299) {
@@ -166,11 +195,11 @@ export default class CallManager {
this.updateTokens(access_token, refresh_token);
VoxeetSDK.initializeToken(access_token, refresh_token);
} else {
console.error('Get Voxeet Token API Result Error', tokenResult);
console.error("Get Voxeet Token API Result Error", tokenResult);
}
})
.catch((err) => {
console.error('Get Voxeet Token API error', err);
console.error("Get Voxeet Token API error", err);
});
} else {
VoxeetSDK.initializeToken(this.__accessToken, this.__refreshToken);
@@ -186,7 +215,7 @@ export default class CallManager {
resolve(access_token);
})
.catch((err) => {
console.error('Get Voxeet Token API error', err);
console.error("Get Voxeet Token API error", err);
reject(err);
});
});
+271 -72
View File
@@ -5,16 +5,9 @@ Proprietary and confidential
Written by Ayushya Amitabh <admin@chromewatchparty.com>, 2020
https://www.chromewatchparty.com/copyright
=========================================================================
ALL CONTENT ON IN WATCH PARTY'S CHROME EXTENSION APP, UNLESS OTHERWISE
ALL CONTENT ON WATCH PARTY'S WEBSITE OR IN WATCH PARTY'S CHROME EXTENSION APP, UNLESS OTHERWISE
CITED ARE THE SOLE INTELECTUAL PROPERTY OF AYUSHYA AMITABH, AND MAY NOT BE
COPIED, EDITED, OR REPRODUCED WITHOUT PERMISSION.
Netflix logo is a trademark and intellectual property of Netflix, Inc.
Disney+ logo is a trademark and intellectual property of The Walt Disney Company
Prime logo is a trademark and intellectual property of Amazon.com, Inc.
YouTube logo is a trademark and intellectual property of Google, Inc.
Hulu logo is a trademark and intellectual property of Hulu, LLC.
HBO Now and HBO Max logos are trademark and intellectual property of WarnerMedia Direct,LLC.
************************************************************************/
/*global chrome */
import React, { Component } from 'react';
@@ -35,10 +28,11 @@ import {
FormControlLabel,
Chip,
CircularProgress,
Paper,
Grid,
Slider,
} from '@material-ui/core';
import Thread from './Thread';
import { Carousel } from '@giphy/react-components';
import { GiphyFetch } from '@giphy/js-fetch-api';
import {
SendMessageApi,
LeaveRoomApi,
@@ -48,8 +42,10 @@ import {
PLAYER_SOURCE,
LocalData,
PLAYER_STATUS,
emptyLocalData,
updateLocalData,
UpdateAdminOnly,
scrollThread,
removeLocalData,
} from './Api';
import {
Send,
@@ -69,24 +65,17 @@ import {
Error,
FullscreenExit,
Fullscreen,
VolumeDown,
VolumeUp,
CancelRounded,
} from '@material-ui/icons';
import Create from './Create';
import { goTo } from 'route-lite';
import CallManager from './CallManager';
import VideoListener from './VideoListener';
import ReactGiphySearchbox from 'react-giphy-searchbox';
const giphyFetch = new GiphyFetch('ojJbIMUN6XRMn4mHiFFav3rMCpXUg8sq');
const alerts = [
{
condition: () =>
getPlayerSource() === PLAYER_SOURCE.HBOMAX || getPlayerSource() === PLAYER_SOURCE.HBONOW,
message: 'Functionality may be buggy, HBO has a horrible design.',
},
{
condition: () => getPlayerSource() === PLAYER_SOURCE.PEACOCK,
message: 'Functionality may be buggy, support on this site is in testing.',
},
];
// const giphyFetch = new GiphyFetch('ojJbIMUN6XRMn4mHiFFav3rMCpXUg8sq');
export default class Chat extends Component {
constructor(props) {
super(props);
@@ -95,6 +84,8 @@ export default class Chat extends Component {
messages: [],
newMessage: '',
admin: null,
adminOnly: false,
adminModeUpdate: false,
users: [],
usersOnCall: {},
gif: false,
@@ -107,6 +98,9 @@ export default class Chat extends Component {
isFullscreen: document.fullscreen,
allowAutoRejoin:
LocalData().allowAutoRejoin !== undefined ? LocalData().allowAutoRejoin : true,
replyTo: null,
sendingMessages: [],
volumes: {},
};
this.port = null;
this.gifClick = this.gifClick.bind(this);
@@ -115,9 +109,11 @@ export default class Chat extends Component {
this.leaveParty = this.leaveParty.bind(this);
this.shareParty = this.shareParty.bind(this);
this.syncPlayer = this.syncPlayer.bind(this);
this.setReplyTo = this.setReplyTo.bind(this);
this.sendMessage = this.sendMessage.bind(this);
this.getCarousel = this.getCarousel.bind(this);
this.updateAdminOnly = this.updateAdminOnly.bind(this);
this.handlePhoneCall = this.handlePhoneCall.bind(this);
this.getReplyToContent = this.getReplyToContent.bind(this);
this.callEventListener = this.callEventListener.bind(this);
this.sendMessageInvoker = this.sendMessageInvoker.bind(this);
this.videoEventListener = this.videoEventListener.bind(this);
@@ -167,6 +163,7 @@ export default class Chat extends Component {
this.port.postMessage({
type: 'INIT',
roomId: LocalData().partyName,
roomPin: LocalData().partyPin,
});
// Listener
this.port.onMessage.addListener((response) => {
@@ -175,22 +172,38 @@ export default class Chat extends Component {
goTo(Create, this.props);
return;
}
if (response.messages) {
scrollThread();
}
// IF CHAT IS HIDDEN AND ALLOWS NOTIFICATION
if (!document.querySelector('body.visible') && this.state.allowChatNotification === true) {
if (response.messages && response.messages.length !== this.state.messages.length) {
const lastMessage = response.messages[response.messages.length - 1];
var notificationMessage = '';
if (lastMessage.uid !== '<system>') notificationMessage += lastMessage.name + ': ';
notificationMessage += lastMessage.text;
this.props.notify(notificationMessage);
if (lastMessage.uid !== '<system>') {
this.props.notify(
`${lastMessage.name}${
lastMessage.type === 'giphy' ? ' sent a gif.' : ': ' + lastMessage.text
}`
);
}
}
}
// ADMIN MODE UPDATE COMPLETE
if (response.adminOnly !== this.state.adminOnly) {
this.setState({ adminModeUpdate: false });
}
this.setState(response);
if (response.player) this.mVideoListener.doUpdate(response.player);
const pcc = document.querySelector('.thread-container');
if (pcc) pcc.scrollTop = pcc.scrollHeight;
});
// GIPHY IPC
const giphyInject = `var giphySearchInteraction=function(e){if(e.source===window){const{data}=e;if(data.watchPartyDirection==='TO_SCRIPT'){if(data.type==='WATCH_PARTY_SEARCH_GIF'){const giphyInput=document.querySelector('.giphy-search-form input');if(giphyInput){giphyInput.value=data.value;giphyInput.dispatchEvent(new Event('change',{bubbles:true}))}}}}};window.addEventListener("message",giphySearchInteraction);`;
const giphyInjectScript = document.createElement('script');
giphyInjectScript.text = giphyInject;
document.head.appendChild(giphyInjectScript);
// DONE WITH MOUNTING TASKS
this.props.updateLoading(false);
}
@@ -199,7 +212,10 @@ export default class Chat extends Component {
this.leaveParty();
}
videoEventListener(video) {
this.syncPlayer(video);
const { adminOnly, admin } = this.state;
if (!adminOnly || (adminOnly && admin)) {
this.syncPlayer(video);
}
}
callEventListener(event) {
this.setState(event);
@@ -234,10 +250,18 @@ export default class Chat extends Component {
const data = {
partyName: LocalData().partyName,
};
if (this.state.onCall)
this.mCallManager.toggleCall(
this.state.onCall,
this.state.conference,
this.state.admin,
this.state.adminOnly
);
DeleteRoomApi(data)
.then((res) => {
this.props.notify('Ended party');
emptyLocalData();
removeLocalData('partyPin');
removeLocalData('partyName');
goTo(Create, this.props);
this.props.updateLoading(false);
})
@@ -254,10 +278,18 @@ export default class Chat extends Component {
partyName: LocalData().partyName,
userUid: LocalData().userUid,
};
if (this.state.onCall)
this.mCallManager.toggleCall(
this.state.onCall,
this.state.conference,
this.state.admin,
this.state.adminOnly
);
LeaveRoomApi(data)
.then((res) => {
this.props.notify('Left party');
emptyLocalData();
removeLocalData('partyPin');
removeLocalData('partyName');
goTo(Create, this.props);
this.props.updateLoading(false);
})
@@ -279,7 +311,9 @@ export default class Chat extends Component {
this.props.notify('Copied party invitation');
}
sendMessage(message) {
const { sendingMessages } = this.state;
const data = {
id: `sending-${sendingMessages.length}`,
roomId: LocalData().partyName,
name: LocalData().userName,
uid: LocalData().userUid,
@@ -288,10 +322,30 @@ export default class Chat extends Component {
type: '',
};
if (this.state.gif === true) data.type = 'giphy';
SendMessageApi(data).catch((err) => {
this.props.notify(err.message || err.statusText);
console.error('Send Message API error', err);
if (this.state.replyTo !== null) data.replyTo = this.state.replyTo.id;
sendingMessages.push(data);
this.setState({
sendingMessages,
});
SendMessageApi(data)
.then(() => {
const { sendingMessages: sm } = this.state;
const rmi = sm.indexOf(data);
sm.splice(rmi, 1);
this.setState({ sendingMessages: sm });
scrollThread();
})
.catch((err) => {
const { sendingMessages: sm } = this.state;
const rmi = sm.indexOf(data);
sm.splice(rmi, 1);
this.setState({ sendingMessages: sm });
scrollThread();
this.props.notify(err.message || err.statusText);
console.error('Send Message API error', err);
});
this.setState({ replyTo: null });
scrollThread();
}
sendMessageInvoker(overrideMessage) {
if (overrideMessage) this.sendMessage(overrideMessage);
@@ -301,17 +355,11 @@ export default class Chat extends Component {
}
this.setState({ newMessage: '' });
}
gifClick(iGif, ev) {
ev.preventDefault();
gifClick(iGif) {
this.sendMessageInvoker(iGif.images.original.url);
}
getCarousel(offset) {
return this.state.newMessage === ''
? giphyFetch.trending({ offset, limit: 1 })
: giphyFetch.search(this.state.newMessage, { offset, limit: 1 });
}
handlePhoneCall() {
const { onCall, conference, admin } = this.state;
const { onCall, conference, admin, adminOnly } = this.state;
if (window.navigator && onCall === false) {
try {
const overlay = document.createElement('div');
@@ -327,7 +375,7 @@ export default class Chat extends Component {
})
.then(() => {
document.getElementById('mic-permission-overlay').remove();
this.mCallManager.toggleCall(onCall, conference, admin);
this.mCallManager.toggleCall(onCall, conference, admin, adminOnly);
})
.catch(() => {
document.getElementById('mic-permission-overlay').remove();
@@ -336,9 +384,9 @@ export default class Chat extends Component {
);
});
} catch (err) {
console.log(err);
console.error('Handle Phone Call Error', err);
}
} else this.mCallManager.toggleCall(onCall, conference, admin);
} else this.mCallManager.toggleCall(onCall, conference, admin, adminOnly);
}
handleMute() {
this.mCallManager.toggleMute(this.state.muted);
@@ -363,6 +411,49 @@ export default class Chat extends Component {
document.documentElement.requestFullscreen();
}
}
updateAdminOnly() {
this.setState({ adminModeUpdate: true });
UpdateAdminOnly({
partyName: this.state.name,
adminOnly: !this.state.adminOnly,
}).catch((err) => {
this.props.notify(err.message || err.statusText);
console.error('Update Admin Only API error', err);
});
}
setReplyTo(message) {
this.setState({
replyTo: message,
});
scrollThread();
}
getReplyToContent() {
const { replyTo } = this.state;
if (replyTo) {
if (replyTo.type === 'giphy') {
return <img alt="gif" src={replyTo.text} />;
}
return (
<Typography variant="body2" gutterBottom>
{replyTo.text}
</Typography>
);
}
return null;
}
setVideoVolume(v, id) {
const audioOuts = document.querySelectorAll('audio.vxt-audio');
if (audioOuts.length > 0) {
audioOuts.forEach((el) => {
if (el.id === `audio-${id}`) {
el.volume = v * 0.1;
const { volumes } = this.state;
volumes[id] = v;
this.setState({ volumes });
}
});
}
}
render() {
const {
admin,
@@ -379,7 +470,31 @@ export default class Chat extends Component {
allowAutoRejoin,
usersOnCall,
isFullscreen,
adminModeUpdate,
adminOnly,
replyTo,
sendingMessages,
volumes,
} = this.state;
const alerts = [
{
condition: () =>
getPlayerSource() === PLAYER_SOURCE.HBOMAX || getPlayerSource() === PLAYER_SOURCE.HBONOW,
message: 'Functionality may be buggy, HBO has a horrible design.',
},
{
condition: () => getPlayerSource() === PLAYER_SOURCE.GOOGLEPLAY,
message: 'Functionality may be buggy, support on this site is in testing.',
},
{
condition: () => getPlayerSource() === PLAYER_SOURCE.CRUNCHYROLL,
message: 'Functionality may be buggy, support on this site is in testing.',
},
{
condition: () => adminOnly,
message: 'Party is currently in admin only mode.',
},
];
return (
<div className="party-chat-container">
<div className="party-chat-header">
@@ -496,6 +611,32 @@ export default class Chat extends Component {
}
labelPlacement="end"
/>
{admin ? (
<FormControlLabel
className="notification-switch"
control={
<Switch
checked={adminOnly}
onChange={this.updateAdminOnly}
name="adminOnly"
color={adminOnly ? 'secondary' : 'primary'}
disabled={adminModeUpdate}
/>
}
label={
<span>
Admin Only Mode
<br />
<span style={{ color: '#8a8a8a' }}>
{adminOnly
? 'Only admin can update player and start calls right now'
: 'All party members can update player and start calls right now'}
</span>
</span>
}
labelPlacement="end"
/>
) : null}
<Typography className="party-controls-users-title" variant="overline">
PARTY MEMBERS
</Typography>
@@ -514,17 +655,41 @@ export default class Chat extends Component {
{Object.keys(usersOnCall).map((id) => {
return usersOnCall[id].status === 'Connected' ||
usersOnCall[id].status === 'Connecting' ? (
<Chip
color={
usersOnCall[id].info.externalId === LocalData().userUid
? 'secondary'
: 'default'
<Tooltip
interactive
title={
<Grid className="party-controls-users-volume" container spacing={2}>
<Grid item>
<VolumeDown />
</Grid>
<Grid item xs>
<Slider
max={10}
min={0}
step={1}
color="secondary"
value={volumes[id] || 10}
onChangeCommitted={(e, v) => this.setVideoVolume(v, id)}
/>
</Grid>
<Grid item>
<VolumeUp />
</Grid>
</Grid>
}
className="party-controls-users-chip"
clickable
size="small"
label={usersOnCall[id].info.name || ' '}
/>
>
<Chip
color={
usersOnCall[id].info.externalId === LocalData().userUid
? 'secondary'
: 'default'
}
className="party-controls-users-chip"
clickable
size="small"
label={usersOnCall[id].info.name || ' '}
/>
</Tooltip>
) : null;
})}
</div>
@@ -570,7 +735,7 @@ export default class Chat extends Component {
</Card>
) : null}
</div>
<div className="thread-container">
<div className="thread-wrapper">
<div className="alerts-container">
{alerts.map((a) => {
if (a.condition() === true) {
@@ -585,8 +750,27 @@ export default class Chat extends Component {
} else return null;
})}
</div>
<Thread messages={messages} />
<Thread
setReplyTo={this.setReplyTo}
sendingMessages={sendingMessages}
sentMessages={messages}
/>
</div>
<Paper
square
variant="outlined"
className={`replying-to-card ${replyTo !== null ? 'replying-to-card-up' : ''}`}
>
<div className="replying-to-content">
<Typography variant="overline" display="block" gutterBottom>
REPLYING TO
</Typography>
{this.getReplyToContent()}
</div>
<IconButton onClick={() => this.setReplyTo(null)}>
<CancelRounded />
</IconButton>
</Paper>
<div className="text-phone-panel">
{gif || newMessage ? null : (
<Tooltip title="Fullscreen with Watch Party" aria-label="Fullscreen">
@@ -656,9 +840,13 @@ export default class Chat extends Component {
value={newMessage}
onChange={(e) => {
this.setState({ newMessage: e.target.value });
if (this.state.gif) {
const carousel = document.querySelector('.giphy-carousel');
setTimeout(() => carousel.scroll(carousel.scrollWidth, carousel.scrollHeight), 250);
scrollThread();
if (gif) {
window.postMessage({
watchPartyDirection: 'TO_SCRIPT',
type: 'WATCH_PARTY_SEARCH_GIF',
value: e.target.value,
});
}
}}
InputProps={{
@@ -681,7 +869,17 @@ export default class Chat extends Component {
<InputAdornment position="start">
<IconButton
className={`gif-button ${this.state.gif ? 'gif-close' : ''}`}
onClick={() => this.setState({ gif: !gif })}
onClick={() => {
this.setState({ gif: !gif });
if (!gif) {
window.postMessage({
watchPartyDirection: 'TO_SCRIPT',
type: 'WATCH_PARTY_SEARCH_GIF',
value: newMessage,
});
scrollThread();
}
}}
edge="start"
>
{gif ? <Cancel /> : <Gif />}
@@ -691,15 +889,16 @@ export default class Chat extends Component {
}}
/>
</div>
{gif ? (
<Carousel
fetchGifs={this.getCarousel}
gifHeight={200}
gutter={6}
onGifClick={this.gifClick}
hideAttribution
/>
) : null}
<ReactGiphySearchbox
apiKey="ojJbIMUN6XRMn4mHiFFav3rMCpXUg8sq"
onSelect={this.gifClick}
masonryConfig={[{ columns: 2, imageWidth: '50%', gutter: 5 }]}
searchFormClassName="giphy-search-form"
wrapperClassName={`giphy-wrapper ${gif ? '' : 'giphy-wrapper-down'}`}
listWrapperClassName="giphy-list-wrapper"
listItemClassName="giphy-list-item"
poweredByGiphy
/>
</div>
);
}
-95
View File
@@ -1,95 +0,0 @@
/************************************************************************
Copyright (C) Ayushya Amitabh - All Rights Reserved
Unauthorized copying of this file, via any medium is strictly prohibited
Proprietary and confidential
Written by Ayushya Amitabh <admin@chromewatchparty.com>, 2020
https://www.chromewatchparty.com/copyright
=========================================================================
ALL CONTENT ON IN WATCH PARTY'S CHROME EXTENSION APP, UNLESS OTHERWISE
CITED ARE THE SOLE INTELECTUAL PROPERTY OF AYUSHYA AMITABH, AND MAY NOT BE
COPIED, EDITED, OR REPRODUCED WITHOUT PERMISSION.
Netflix logo is a trademark and intellectual property of Netflix, Inc.
Disney+ logo is a trademark and intellectual property of The Walt Disney Company
Prime logo is a trademark and intellectual property of Amazon.com, Inc.
YouTube logo is a trademark and intellectual property of Google, Inc.
Hulu logo is a trademark and intellectual property of Hulu, LLC.
HBO Now and HBO Max logos are trademark and intellectual property of WarnerMedia Direct,LLC.
************************************************************************/
/************************************************************************
THE CODE BELOW WAS WRITTEN BY USER
https://stackoverflow.com/users/68808/tweezz
ON STACKOVERFLOW, WHICH CAN BE FOUND AT
https://stackoverflow.com/questions/6157929/how-to-simulate-a-mouse-click-using-javascript
************************************************************************/
export default function simulate(element, eventName) {
var options = extend(defaultOptions, arguments[2] || {});
var oEvent,
eventType = null;
for (var name in eventMatchers) {
if (eventMatchers[name].test(eventName)) {
eventType = name;
break;
}
}
if (!eventType) throw new SyntaxError('Only HTMLEvents and MouseEvents interfaces are supported');
if (document.createEvent) {
oEvent = document.createEvent(eventType);
if (eventType == 'HTMLEvents') {
oEvent.initEvent(eventName, options.bubbles, options.cancelable);
} else {
oEvent.initMouseEvent(
eventName,
options.bubbles,
options.cancelable,
document.defaultView,
options.button,
options.pointerX,
options.pointerY,
options.pointerX,
options.pointerY,
options.ctrlKey,
options.altKey,
options.shiftKey,
options.metaKey,
options.button,
element
);
}
element.dispatchEvent(oEvent);
} else {
options.clientX = options.pointerX;
options.clientY = options.pointerY;
var evt = document.createEventObject();
oEvent = extend(evt, options);
element.fireEvent('on' + eventName, oEvent);
}
return element;
}
function extend(destination, source) {
for (var property in source) destination[property] = source[property];
return destination;
}
const eventMatchers = {
HTMLEvents: /^(?:load|unload|abort|error|select|change|submit|reset|focus|blur|resize|scroll)$/,
MouseEvents: /^(?:click|dblclick|mouse(?:down|up|over|move|out))$/,
};
const defaultOptions = {
pointerX: 0,
pointerY: 0,
button: 0,
ctrlKey: false,
altKey: false,
shiftKey: false,
metaKey: false,
bubbles: true,
cancelable: true,
};
+269 -20
View File
@@ -5,19 +5,29 @@ Proprietary and confidential
Written by Ayushya Amitabh <admin@chromewatchparty.com>, 2020
https://www.chromewatchparty.com/copyright
=========================================================================
ALL CONTENT ON IN WATCH PARTY'S CHROME EXTENSION APP, UNLESS OTHERWISE
ALL CONTENT ON WATCH PARTY'S WEBSITE OR IN WATCH PARTY'S CHROME EXTENSION APP, UNLESS OTHERWISE
CITED ARE THE SOLE INTELECTUAL PROPERTY OF AYUSHYA AMITABH, AND MAY NOT BE
COPIED, EDITED, OR REPRODUCED WITHOUT PERMISSION.
Netflix logo is a trademark and intellectual property of Netflix, Inc.
Disney+ logo is a trademark and intellectual property of The Walt Disney Company
Prime logo is a trademark and intellectual property of Amazon.com, Inc.
YouTube logo is a trademark and intellectual property of Google, Inc.
Hulu logo is a trademark and intellectual property of Hulu, LLC.
HBO Now and HBO Max logos are trademark and intellectual property of WarnerMedia Direct,LLC.
************************************************************************/
/*global chrome */
import React, { Component } from 'react';
import { Button, TextField, Typography, Paper, ButtonGroup } from '@material-ui/core';
import {
Button,
FormControlLabel,
Switch,
TextField,
Typography,
Paper,
ButtonGroup,
ExpansionPanel,
ExpansionPanelSummary,
ExpansionPanelDetails,
MobileStepper,
IconButton,
Card,
CardContent,
CardActions,
} from '@material-ui/core';
import {
JoinRoomApi,
CreateRoomApi,
@@ -26,6 +36,7 @@ import {
LocalData,
updateLocalData,
getUserUid,
emptyLocalData,
} from './Api';
import { goTo } from 'route-lite';
import Chat from './Chat';
@@ -34,7 +45,13 @@ import {
InfoRounded,
MonetizationOnRounded,
RateReviewRounded,
Settings,
ExpandLess,
KeyboardArrowLeft,
KeyboardArrowRight,
Notifications,
} from '@material-ui/icons';
import { Autocomplete } from '@material-ui/lab';
const CONSTANTS = {
USER_NAME_LENGTH: 12,
@@ -50,10 +67,26 @@ export default class Create extends Component {
name: LocalData().partyName || '',
pin: LocalData().partyPin || '',
mode: defaultMode,
parties: [],
adminOnly: false,
alertStep:
this.props.status && this.props.status.importantAlert
? this.props.status.importantAlert
: 0,
allowAutoRejoin:
LocalData().allowAutoRejoin !== undefined ? LocalData().allowAutoRejoin : true,
allowChatNotification:
LocalData().allowChatNotification !== undefined ? LocalData().allowChatNotification : true,
expanded: 'alerts',
};
this.createCall = this.createCall.bind(this);
this.joinCall = this.joinCall.bind(this);
this.success = this.success.bind(this);
this.partyNameAutocompleteProcessor = this.partyNameAutocompleteProcessor.bind(this);
this.updateAllowAutoRejoin = this.updateAllowAutoRejoin.bind(this);
this.updateAllowChatNotification = this.updateAllowChatNotification.bind(this);
this.clearPartyHistory = this.clearPartyHistory.bind(this);
this.handleSteps = this.handleSteps.bind(this);
}
componentDidMount() {
const { lastActive, userName, partyName, partyPin, allowAutoRejoin } = LocalData();
@@ -63,6 +96,14 @@ export default class Create extends Component {
this.joinCall(userName, partyName, partyPin);
}
}
chrome.runtime.sendMessage(
{
type: 'GET_PARTIES',
},
(response) => {
this.setState({ parties: response });
}
);
}
partyNameChange(value) {
if (value.length <= CONSTANTS.NAME_LENGTH) this.setState({ name: value });
@@ -121,6 +162,7 @@ export default class Create extends Component {
time: 0,
url: window.location.href,
},
adminOnly: this.state.adminOnly,
};
CreateRoomApi(apidata)
.then((res) => res.json())
@@ -157,6 +199,7 @@ export default class Create extends Component {
}
})
.catch((err) => {
emptyLocalData();
console.error('Join Party API Error', err);
this.props.notify(err.message || 'Something went wrong');
this.props.updateLoading(false);
@@ -167,8 +210,58 @@ export default class Create extends Component {
this.props.updateLoading(false);
goTo(Chat, this.props);
}
partyNameAutocompleteProcessor(v) {
if (v !== null && v !== undefined && typeof v === 'object') {
if (v.name) this.partyNameChange(v.name);
else this.partyNameChange('');
if (v.pin) this.partyPinChange(v.pin);
else this.partyPinChange('');
} else if (typeof v === 'object' && (v === null || v === undefined)) {
this.partyNameChange('');
this.partyPinChange('');
} else if (v !== undefined && v !== null && typeof v === 'string') {
this.partyNameChange(v);
}
}
updateAllowChatNotification() {
updateLocalData({ allowChatNotification: !this.state.allowChatNotification });
this.setState({ allowChatNotification: !this.state.allowChatNotification });
}
updateAllowAutoRejoin() {
updateLocalData({ allowAutoRejoin: !this.state.allowAutoRejoin });
this.setState({ allowAutoRejoin: !this.state.allowAutoRejoin });
}
clearPartyHistory() {
this.setState({
parties: [],
});
chrome.runtime.sendMessage({ type: 'EMPTY_PARTIES' });
}
handleSteps(direction) {
const { alertStep } = this.state;
if (alertStep + direction < 0) return;
if (alertStep + direction >= this.props.status.alerts.length) return;
this.setState({ alertStep: alertStep + direction });
}
handleExpanders(e) {
const { expanded } = this.state;
if (e === expanded) this.setState({ expanded: '' });
else this.setState({ expanded: e });
}
render() {
const { mode, name, pin, userName } = this.state;
const {
mode,
name,
pin,
userName,
parties,
adminOnly,
allowAutoRejoin,
allowChatNotification,
alertStep,
expanded,
} = this.state;
const { status } = this.props;
return (
<div className="party-create-container">
<Typography
@@ -213,15 +306,26 @@ export default class Create extends Component {
userName ? `${userName.length}/${CONSTANTS.USER_NAME_LENGTH} Characters` : null
}
/>
<TextField
<Autocomplete
color="secondary"
size="small"
className="create-name addFocus"
label="Party Name"
variant="outlined"
value={name}
onChange={(e) => this.partyNameChange(e.target.value)}
helperText={name ? `${name.length}/${CONSTANTS.NAME_LENGTH} Characters` : null}
options={parties}
getOptionLabel={(o) => o.name}
autoComplete
freeSolo
value={{ name: name, pin: pin }}
onInputChange={(e, v, r) => this.partyNameAutocompleteProcessor({ name: v, pin: pin })}
onChange={(e, v) => this.partyNameAutocompleteProcessor(v)}
renderInput={(params) => (
<TextField
color="secondary"
size="small"
{...params}
className="create-name addFocus"
label="Party Name"
variant="outlined"
helperText={name ? `${name.length}/${CONSTANTS.NAME_LENGTH} Characters` : null}
/>
)}
/>
<TextField
color="secondary"
@@ -234,6 +338,31 @@ export default class Create extends Component {
onChange={(e) => this.partyPinChange(e.target.value)}
helperText={pin ? `${pin.length}/${CONSTANTS.PIN_LENGTH} Digits` : null}
/>
<FormControlLabel
className={`notification-switch admin-only-switch ${
mode !== 'create' ? 'admin-only-switch-hidden' : ''
}`}
control={
<Switch
checked={adminOnly}
onChange={() => this.setState({ adminOnly: !adminOnly })}
name="adminOnly"
color={adminOnly ? 'secondary' : 'primary'}
/>
}
label={
<span>
Admin Only Mode (can be changed later)
<br />
<span style={{ color: '#8a8a8a' }}>
{adminOnly
? 'Only admin will update currently playing and start calls'
: 'All members will update currently playing and start calls'}
</span>
</span>
}
labelPlacement="end"
/>
<Button
size="small"
variant="contained"
@@ -243,13 +372,133 @@ export default class Create extends Component {
<span>{mode === 'create' ? 'Start Party' : 'Join Party'}</span> <ChevronRight />
</Button>
</Paper>
<ExpansionPanel expanded={expanded === 'alerts'} className="create-settings-card">
<ExpansionPanelSummary
onClick={() => this.handleExpanders('alerts')}
expandIcon={<ExpandLess />}
>
<Notifications />
<Typography className="create-settings-title" variant="button">
Alerts
</Typography>
</ExpansionPanelSummary>
<ExpansionPanelDetails>
{status ? (
<div className="alerts-stepper-container">
<Card className="alerts-stepper-card" elevation={10}>
<CardContent>
<Typography color="textSecondary" gutterBottom variant="overline">
{status.alerts[alertStep].title}
</Typography>
<Typography variant="body2" component="p">
{status.alerts[alertStep].description}
</Typography>
</CardContent>
{status.alerts[alertStep].link && status.alerts[alertStep].linkText && (
<CardActions>
<Button href={status.alerts[alertStep].link} target="_blank" size="small">
{status.alerts[alertStep].linkText}
</Button>
</CardActions>
)}
</Card>
<MobileStepper
className="alerts-stepper-controls"
variant="dots"
color="secondary"
activeStep={alertStep}
backButton={
<IconButton disabled={alertStep === 0} onClick={() => this.handleSteps(-1)}>
<KeyboardArrowLeft />
</IconButton>
}
nextButton={
<IconButton
disabled={alertStep === status.alerts.length - 1}
onClick={() => this.handleSteps(1)}
>
<KeyboardArrowRight />
</IconButton>
}
steps={status.alerts ? status.alerts.length : 0}
/>
</div>
) : (
<Typography color="textSecondary" gutterBottom variant="overline">
No alerts right now
</Typography>
)}
</ExpansionPanelDetails>
</ExpansionPanel>
<ExpansionPanel expanded={expanded === 'settings'} className="create-settings-card">
<ExpansionPanelSummary
onClick={() => this.handleExpanders('settings')}
expandIcon={<ExpandLess />}
>
<Settings />
<Typography className="create-settings-title" variant="button">
Settings
</Typography>
</ExpansionPanelSummary>
<ExpansionPanelDetails>
<FormControlLabel
className="notification-switch"
control={
<Switch
checked={allowChatNotification}
onChange={this.updateAllowChatNotification}
name="allowChatNotification"
/>
}
label={
<span>
Allow chat notification when side panel is closed
<br />
<span style={{ color: '#8a8a8a' }}>
{allowChatNotification ? 'Notifications Enabled' : 'Notifications Disabled'}
</span>
</span>
}
labelPlacement="end"
/>
<FormControlLabel
className="notification-switch"
control={
<Switch
checked={allowAutoRejoin}
onChange={this.updateAllowAutoRejoin}
name="allowAutoRejoin"
/>
}
label={
<span>
Allow Watch Party to auto rejoin (within 1 minute)
<br />
<span style={{ color: '#8a8a8a' }}>
{allowAutoRejoin ? 'Auto Rejoin Enabled' : 'Auto Rejoin Disabled'}
</span>
</span>
}
labelPlacement="end"
/>
<Button
color="secondary"
fullWidth
variant="contained"
onClick={this.clearPartyHistory}
size="small"
>
CLEAR SAVED PARTY DETAILS
</Button>
</ExpansionPanelDetails>
</ExpansionPanel>
<Button
target="_blank"
href={`http://www.chromewatchparty.com/changes/v${this.props.appVersion}`}
fullWidth
color="secondary"
variant="outlined"
size="large"
variant="contained"
size="small"
className="whats-new-button"
>
See what's new in V{this.props.appVersion}
+1 -8
View File
@@ -5,16 +5,9 @@ Proprietary and confidential
Written by Ayushya Amitabh <admin@chromewatchparty.com>, 2020
https://www.chromewatchparty.com/copyright
=========================================================================
ALL CONTENT ON IN WATCH PARTY'S CHROME EXTENSION APP, UNLESS OTHERWISE
ALL CONTENT ON WATCH PARTY'S WEBSITE OR IN WATCH PARTY'S CHROME EXTENSION APP, UNLESS OTHERWISE
CITED ARE THE SOLE INTELECTUAL PROPERTY OF AYUSHYA AMITABH, AND MAY NOT BE
COPIED, EDITED, OR REPRODUCED WITHOUT PERMISSION.
Netflix logo is a trademark and intellectual property of Netflix, Inc.
Disney+ logo is a trademark and intellectual property of The Walt Disney Company
Prime logo is a trademark and intellectual property of Amazon.com, Inc.
YouTube logo is a trademark and intellectual property of Google, Inc.
Hulu logo is a trademark and intellectual property of Hulu, LLC.
HBO Now and HBO Max logos are trademark and intellectual property of WarnerMedia Direct,LLC.
************************************************************************/
import React from 'react';
import { Typography } from '@material-ui/core';
+2 -9
View File
@@ -5,23 +5,16 @@ Proprietary and confidential
Written by Ayushya Amitabh <admin@chromewatchparty.com>, 2020
https://www.chromewatchparty.com/copyright
=========================================================================
ALL CONTENT ON IN WATCH PARTY'S CHROME EXTENSION APP, UNLESS OTHERWISE
ALL CONTENT ON WATCH PARTY'S WEBSITE OR IN WATCH PARTY'S CHROME EXTENSION APP, UNLESS OTHERWISE
CITED ARE THE SOLE INTELECTUAL PROPERTY OF AYUSHYA AMITABH, AND MAY NOT BE
COPIED, EDITED, OR REPRODUCED WITHOUT PERMISSION.
Netflix logo is a trademark and intellectual property of Netflix, Inc.
Disney+ logo is a trademark and intellectual property of The Walt Disney Company
Prime logo is a trademark and intellectual property of Amazon.com, Inc.
YouTube logo is a trademark and intellectual property of Google, Inc.
Hulu logo is a trademark and intellectual property of Hulu, LLC.
HBO Now and HBO Max logos are trademark and intellectual property of WarnerMedia Direct,LLC.
************************************************************************/
import React from 'react';
import { CircularProgress } from '@material-ui/core';
const Loading = () => (
<div id="party-loading">
<CircularProgress />
<CircularProgress color="secondary" />
</div>
);
+262 -90
View File
@@ -5,106 +5,278 @@ Proprietary and confidential
Written by Ayushya Amitabh <admin@chromewatchparty.com>, 2020
https://www.chromewatchparty.com/copyright
=========================================================================
ALL CONTENT ON IN WATCH PARTY'S CHROME EXTENSION APP, UNLESS OTHERWISE
ALL CONTENT ON WATCH PARTY'S WEBSITE OR IN WATCH PARTY'S CHROME EXTENSION APP, UNLESS OTHERWISE
CITED ARE THE SOLE INTELECTUAL PROPERTY OF AYUSHYA AMITABH, AND MAY NOT BE
COPIED, EDITED, OR REPRODUCED WITHOUT PERMISSION.
Netflix logo is a trademark and intellectual property of Netflix, Inc.
Disney+ logo is a trademark and intellectual property of The Walt Disney Company
Prime logo is a trademark and intellectual property of Amazon.com, Inc.
YouTube logo is a trademark and intellectual property of Google, Inc.
Hulu logo is a trademark and intellectual property of Hulu, LLC.
HBO Now and HBO Max logos are trademark and intellectual property of WarnerMedia Direct,LLC.
************************************************************************/
.thread-container * {
margin: 0;
padding: 0;
font-family: "Roboto", sans-serif !important;
letter-spacing: 1.15px;
word-break: break-word !important;
}
.thread-container {
max-height: none;
padding: 0 15px;
padding-right: 0px !important;
overflow: hidden;
}
.thread-item {
width: 100%;
white-space: pre-wrap;
margin-top: 25px !important;
font-size: 12px !important;
position: relative;
}
.thread-item.no-space {
margin-top: 0 !important;
}
.system-message {
display: block;
text-align: center;
width: 100%;
text-align: center;
font-size: 12px;
margin-top: 7.5px !important;
color: rgba(147, 147, 147, 0.8) !important;
}
.system-message.alert {
background-color: black;
border-radius: 1rem;
color: white !important;
padding: 5px 10px;
position: sticky;
top: 2px;
z-index: 1;
}
.my-message {
display: block;
color: white;
width: fit-content;
text-align: right;
margin-left: auto !important;
margin-right: 5px !important;
margin-top: 5px !important;
padding: 2.5px 10px !important;
border-radius: 13px;
max-width: 60%;
word-wrap: break-word;
background-color: rgb(49, 49, 49);
font-size: 14px !important;
}
img.my-message {
margin-right: 0 !important;
padding-right: 0 !important;
border-radius: 1rem;
max-width: 60%;
background: none;
border-radius: 2rem;
}
.party-message {
font-size: 14px !important;
display: block;
width: fit-content;
max-width: 60%;
word-wrap: break-word;
margin-top: 5px !important;
padding: 2.5px 10px !important;
border-radius: 13px;
background-color: rgb(88, 88, 88);
}
img.party-message {
border-radius: 1rem;
max-width: 60%;
background: none;
border-radius: 2rem;
margin-left: 0 !important;
padding-left: 0 !important;
}
.thread-item .name {
color: rgb(149, 149, 149);
font-size: 12px !important;
}
.time {
top: 0;
color: #3a3a3a;
position: absolute;
opacity: 0;
margin: 10px 0;
width: 100%;
transition: all ease-in-out 0.3s;
}
.party-message ~ .time {
right: 0;
.thread-item.highlight .thread-message img,
.thread-item.highlight .thread-message p {
border: 2px solid #c0f300;
}
.party-message:hover ~ .time {
opacity: 1;
.thread-item.highlight .thread-message img {
padding: 7.5px 10px;
}
.my-message ~ .time {
.thread-message {
width: 100%;
position: relative;
}
.thread-message .thread-overline {
height: 0;
overflow-y: visible;
font-size: 10px;
transition: all ease-in-out 0.3s;
transition-delay: 0.1s;
white-space: nowrap;
display: flex;
align-items: center;
opacity: 0;
color: #9f9f9f;
width: 100%;
display: flex;
justify-content: center;
align-items: center;
flex-direction: row-reverse;
}
.thread-message .thread-overline span {
flex: 1;
text-align: center;
}
.thread-message .thread-overline .thread-reply-icon {
position: absolute;
display: flex;
justify-content: center;
align-items: center;
font-size: 20px;
border-radius: 26px;
z-index: 999 !important;
white-space: break-spaces;
left: 0;
cursor: pointer;
bottom: 0;
transition: all ease-in-out 0.3s;
transition-delay: 0.1s;
width: 25px;
height: 25px;
padding: 0;
padding-bottom: 7px;
}
.my-message:hover ~ .time {
.thread-item:hover .thread-message .thread-overline {
height: 24px;
margin-bottom: 7px;
opacity: 1;
}
.thread-message p {
max-width: 65%;
padding: 5px 15px;
background: #424242;
border-radius: 26px;
z-index: 9999 !important;
transition: all ease-in-out 0.3s;
transition-delay: 0.1s;
width: fit-content;
min-height: 25px;
display: flex;
align-items: center;
line-height: 16px;
font-size: 13px;
font-weight: 100;
font-family: "Roboto";
letter-spacing: 1.5px;
white-space: break-spaces;
word-break: break-all;
}
.thread-message img {
max-width: 55%;
border-radius: 26px;
transition: all ease-in-out 0.3s;
transition-delay: 0.1s;
z-index: 999;
}
.thread-item:hover .thread-message p,
.thread-item:hover .thread-message img {
margin-left: 40px;
}
.thread-message .thread-overline .thread-reply-icon:hover {
opacity: 0.6;
}
.thread-name {
font-size: 11px;
margin-top: 15px;
margin-bottom: 7px;
color: #adadad;
}
.thread-reply {
font-size: 11px;
margin-bottom: -20px;
padding: 7.5px 15px;
padding-bottom: 25px;
border-radius: 13px;
background: #222222;
width: fit-content;
max-width: 45%;
z-index: 999 !important;
transition: all ease-in-out 0.3s;
transition-delay: 0.1s;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
cursor: pointer;
}
.thread-reply-name {
color: #8a8a8a;
font-size: 10px;
text-transform: uppercase;
margin-bottom: 5px !important;
}
.thread-reply p {
width: fit-content;
max-width: 100%;
overflow: hidden;
text-overflow: ellipsis;
}
.thread-reply img {
max-height: 70px;
border-radius: 26px;
transition: all ease-in-out 0.3s;
transition-delay: 0.1s;
}
.thread-item:hover .thread-reply {
margin-bottom: 0;
padding-bottom: 7.5px;
}
.thread-reply:hover {
background: #111111;
}
.thread-item.continued-message .thread-name {
display: none;
}
/*
M Y
M E S S A G E S
*/
.thread-item.my-message {
display: flex;
flex-direction: column;
align-items: flex-end;
}
.thread-item.my-message .thread-name {
display: none;
}
.thread-item.my-message .thread-message {
display: flex;
flex-direction: column;
align-items: flex-end;
}
.thread-item.my-message .thread-message p {
text-align: right;
}
.thread-item.my-message .thread-message .thread-overline {
flex-direction: row;
}
.thread-item.my-message .thread-message .thread-overline .thread-reply-icon {
right: 0;
left: unset;
}
.thread-item.my-message:hover .thread-message p,
.thread-item.my-message:hover .thread-message img {
margin-left: 0;
margin-right: 40px;
}
/*
S Y S T E M
M E S S A G E S
*/
.thread-item.system-message .thread-name,
.thread-item.system-message .thread-reply,
.thread-item.system-message img,
.thread-item.system-message .thread-overline {
display: none;
}
.thread-item.system-message .thread-message p {
width: 100%;
margin: 0 auto;
background: none;
font-size: 12px;
color: #9f9f9f;
text-align: center;
display: block;
}
.thread-item.system-message:hover .thread-message p {
margin: 0 auto;
}
/*
I N T E R A C T I V E
S Y S T E M
M E S S A G E S
*/
.thread-interactive-text {
display: flex !important;
flex-direction: column !important;
margin: 0 auto !important;
background-color: #222222 !important;
border-radius: 42px !important;
padding: 20px 35px !important;
transition: all ease-in-out 0.3s;
transition-delay: 0.1s;
}
.thread-interactive-text:hover {
background-color: #111111 !important;
}
.thread-interactive-question {
font-size: 10px !important;
letter-spacing: 2px !important;
color: #9f9f9f !important;
line-height: 1.5 !important;
padding: 7.5px 0px !important;
padding-top: 0 !important;
text-align: center !important;
}
.thread-interactive-answer > div {
border-radius: 26px;
}
.thread-interactive-thankyou {
text-align: center !important;
}
.thread-interactive-link {
color: inherit !important;
width: calc(100% + 70px) !important;
margin-left: -35px !important;
border-bottom-left-radius: 50px !important;
border-bottom-right-radius: 50px !important;
margin-bottom: -20px !important;
padding-bottom: 15px !important;
margin-top: 5px !important;
}
/*
S E N D I N G
M E S S A G E S
*/
.thread-item.my-message.sending-message .thread-message .thread-overline {
display: none;
}
.thread-item.my-message.sending-message .thread-message p,
.thread-item.my-message.sending-message .thread-message img {
margin-left: 0 !important;
margin-right: 0 !important;
opacity: 0.5;
}
+177 -60
View File
@@ -5,74 +5,191 @@ Proprietary and confidential
Written by Ayushya Amitabh <admin@chromewatchparty.com>, 2020
https://www.chromewatchparty.com/copyright
=========================================================================
ALL CONTENT ON IN WATCH PARTY'S CHROME EXTENSION APP, UNLESS OTHERWISE
ALL CONTENT ON WATCH PARTY'S WEBSITE OR IN WATCH PARTY'S CHROME EXTENSION APP, UNLESS OTHERWISE
CITED ARE THE SOLE INTELECTUAL PROPERTY OF AYUSHYA AMITABH, AND MAY NOT BE
COPIED, EDITED, OR REPRODUCED WITHOUT PERMISSION.
Netflix logo is a trademark and intellectual property of Netflix, Inc.
Disney+ logo is a trademark and intellectual property of The Walt Disney Company
Prime logo is a trademark and intellectual property of Amazon.com, Inc.
YouTube logo is a trademark and intellectual property of Google, Inc.
Hulu logo is a trademark and intellectual property of Hulu, LLC.
HBO Now and HBO Max logos are trademark and intellectual property of WarnerMedia Direct,LLC.
************************************************************************/
import React from 'react';
import { Typography } from '@material-ui/core';
import React, { Component } from 'react';
import {
Typography,
IconButton,
Paper,
TextField,
InputAdornment,
Button,
} from '@material-ui/core';
import { Reply, Send } from '@material-ui/icons';
import './Thread.css';
import { LocalData } from './Api';
import { LocalData, webhookFetcher } from './Api';
const Thread = (props) =>
props.messages.map((m, midx) => {
var { userUid } = LocalData();
if (m.uid === '<system>') {
// SYSTEM MESSAGES
export default class Thread extends Component {
constructor(props) {
super(props);
this.getTimeString = this.getTimeString.bind(this);
this.getReplyMessage = this.getReplyMessage.bind(this);
this.getMessageTextOrImage = this.getMessageTextOrImage.bind(this);
}
scrollToMessage(id) {
const pcc = document.querySelector('.thread-wrapper');
const msg = document.getElementById(id);
const alc = document.querySelector(
'#party-routes-wrapper > div.party-chat-container > div.thread-wrapper > div.alerts-container'
);
pcc.scrollTop = msg.offsetTop - pcc.offsetTop - alc.clientHeight - 10;
msg.classList.add('highlight');
setTimeout(() => msg.classList.remove('highlight'), 2000);
}
handleInteractiveMessageCall(msg) {
const thanksElement = document.createElement('div');
thanksElement.innerText = 'THANK YOU FOR THE FEEDBACK';
thanksElement.className = 'thread-interactive-thankyou';
if (msg.uid === '<system>') {
if (msg.endpoint) {
var bodyData = {
question: msg.text,
};
if (msg.type === 'interactive/text') {
const answerElement = document.getElementById(`reply-${msg.id}`);
bodyData.answer = answerElement ? answerElement.value : 'ANSWER_ELEM_NOT_FOUND';
answerElement.disabled = true;
answerElement.parentElement.parentElement.parentElement.firstElementChild.remove();
answerElement.parentElement.replaceWith(thanksElement);
}
webhookFetcher({
endpoint: msg.endpoint,
body: JSON.stringify(bodyData),
});
}
}
}
getItemClassNames(uid, userUid, msgIdx, messages, isSending = false) {
const lastUid = msgIdx >= 1 ? messages[msgIdx - 1].uid : '';
return `thread-item ${uid === userUid ? 'my-message' : ''} ${
isSending ? 'sending-message' : ''
} ${uid === '<system>' ? 'system-message' : ''} ${lastUid === uid ? 'continued-message' : ''}`;
}
getMessageTextOrImage(msg) {
if (msg.type === 'giphy') {
return <img alt="gif" src={msg.text} />;
}
if (msg.type === 'interactive/text') {
return (
<Typography variant="body2" className="thread-item system-message" key={m.id} disabled>
{m.text}
</Typography>
);
} else if (m.uid === userUid) {
// OWN MESSAGE
return (
<div
className={`thread-item ${
midx > 0 ? (props.messages[midx - 1].uid !== m.uid ? '' : 'no-space') : ''
}`}
key={m.id}
>
{m.type === 'giphy' ? (
<img className="my-message" alt="giphy gif" src={m.text} />
) : (
<Typography variant="body1" className="my-message">
{m.text}
</Typography>
)}
<span className="time">{new Date(m.time.seconds * 1000).toLocaleTimeString()}</span>
</div>
);
} else {
// OTHER USER MESSAGE
return (
<div
className={`thread-item ${
midx > 0 ? (props.messages[midx - 1].uid !== m.uid ? '' : 'no-space') : ''
}`}
key={m.id}
>
<Typography variant="caption" className="name">
{midx > 0 ? (props.messages[midx - 1].uid !== m.uid ? m.name : '') : m.name}
<Paper elevation={0} className="thread-interactive-text">
<Typography className="thread-interactive-question" variant="overline">
{msg.text}
</Typography>
{m.type === 'giphy' ? (
<img className="party-message" alt="giphy gif" src={m.text} />
) : (
<Typography variant="body1" className="party-message">
{m.text}
</Typography>
)}
<span className="time">{new Date(m.time.seconds * 1000).toLocaleTimeString()}</span>
<TextField
className="thread-interactive-answer addFocus"
size="small"
id={`reply-${msg.id}`}
multiline
variant="outlined"
color="secondary"
InputProps={{
endAdornment: (
<InputAdornment position="end">
<IconButton onClick={() => this.handleInteractiveMessageCall(msg)}>
<Send />
</IconButton>
</InputAdornment>
),
}}
rowsMax={2}
/>
</Paper>
);
}
if (msg.type === 'interactive/link') {
return (
<Paper elevation={0} className="thread-interactive-text">
<Typography className="thread-interactive-question" variant="overline">
{msg.text}
</Typography>
<Button
className="thread-interactive-link"
fullWidth
target="_blank"
href={msg.endpoint}
color="secondary"
>
{msg.name}
</Button>
</Paper>
);
}
return <p>{msg.text}</p>;
}
getTimeString(time) {
return new Date(time.seconds * 1000).toLocaleTimeString();
}
getReplyMessage(rT, messages) {
if (rT !== undefined && rT !== null) {
const replyMessage = messages.filter((m) => m.id === rT)[0];
return (
<div className="thread-reply" onClick={() => this.scrollToMessage(replyMessage.id)}>
<p className="thread-reply-name">Reply to {replyMessage.name}:</p>
{this.getMessageTextOrImage(replyMessage)}
</div>
);
}
});
return null;
}
render() {
const { sendingMessages, setReplyTo, sentMessages } = this.props;
const { userUid } = LocalData();
return (
<div className="thread-container">
{sentMessages.length > 0
? sentMessages.map((msg, msgIdx) => (
<div
id={msg.id}
key={`msg-${msgIdx}`}
className={this.getItemClassNames(msg.uid, userUid, msgIdx, sentMessages)}
>
<div className="thread-name">{msg.name}</div>
{this.getReplyMessage(msg.replyTo, sentMessages)}
<div className="thread-message">
<div className="thread-overline">
<span>{this.getTimeString(msg.time)}</span>
<div className="thread-reply-icon">
<IconButton onClick={() => setReplyTo(msg)}>
<Reply />
</IconButton>
</div>
</div>
{this.getMessageTextOrImage(msg)}
</div>
</div>
))
: null}
{sendingMessages.length > 0
? sendingMessages.map((msg, msgIdx) => (
<div
id={msg.id}
key={`msg-${msgIdx}`}
className={this.getItemClassNames(msg.uid, userUid, msgIdx, sendingMessages, true)}
>
<div className="thread-name">{msg.name}</div>
{this.getReplyMessage(msg.replyTo, sentMessages)}
<div className="thread-message">
<div className="thread-overline">
<span>{this.getTimeString(msg.time)}</span>
<div className="thread-reply-icon">
<IconButton onClick={() => setReplyTo(msg)}>
<Reply />
</IconButton>
</div>
</div>
{this.getMessageTextOrImage(msg)}
</div>
</div>
))
: null}
</div>
);
}
}
export default Thread;
Thread.defaultProps = {
setReplyTo: () => {},
};
+182 -43
View File
@@ -5,19 +5,11 @@ Proprietary and confidential
Written by Ayushya Amitabh <admin@chromewatchparty.com>, 2020
https://www.chromewatchparty.com/copyright
=========================================================================
ALL CONTENT ON IN WATCH PARTY'S CHROME EXTENSION APP, UNLESS OTHERWISE
ALL CONTENT ON WATCH PARTY'S WEBSITE OR IN WATCH PARTY'S CHROME EXTENSION APP, UNLESS OTHERWISE
CITED ARE THE SOLE INTELECTUAL PROPERTY OF AYUSHYA AMITABH, AND MAY NOT BE
COPIED, EDITED, OR REPRODUCED WITHOUT PERMISSION.
Netflix logo is a trademark and intellectual property of Netflix, Inc.
Disney+ logo is a trademark and intellectual property of The Walt Disney Company
Prime logo is a trademark and intellectual property of Amazon.com, Inc.
YouTube logo is a trademark and intellectual property of Google, Inc.
Hulu logo is a trademark and intellectual property of Hulu, LLC.
HBO Now and HBO Max logos are trademark and intellectual property of WarnerMedia Direct,LLC.
************************************************************************/
import { PLAYER_STATUS, getPlayerSource, PLAYER_SOURCE, LocalData } from './Api';
import simulate from './ClickSimulator';
const templateListener = (video) => console.log('MISSING VIDEO LISTENER');
@@ -39,6 +31,7 @@ export default class VideoListener {
this.onpause = this.onpause.bind(this);
this.onplay = this.onplay.bind(this);
this.shouldPushSync = this.shouldPushSync.bind(this);
this.crunchyRollListener = this.crunchyRollListener.bind(this);
}
shouldPushSync() {
return (
@@ -73,8 +66,13 @@ export default class VideoListener {
this.isUpdating = true;
if (this.video.element) {
// REMOVE LISTENER BEFORE SYNCING
this.video.element.removeEventListener('playing', this.onplay);
this.video.element.removeEventListener('pause', this.onpause);
switch (this.video.source) {
case PLAYER_SOURCE.CRUNCHYROLL:
window.removeEventListener('watchParty', this.crunchyRollListener);
default:
this.video.element.removeEventListener('playing', this.onplay);
this.video.element.removeEventListener('pause', this.onpause);
}
await this.timeout(250);
// SEEK
if (Math.abs(serverPlayer.time - this.video.element.currentTime) > 1.5) {
@@ -87,6 +85,7 @@ export default class VideoListener {
await this.video.element
.play()
.catch((err) => console.error('WatchParty erred when syncing time.'));
await this.timeout(250);
this.video.element.currentTime = serverPlayer.time;
await this.timeout(500);
break;
@@ -105,14 +104,27 @@ export default class VideoListener {
this.video.element.currentTime = serverPlayer.time;
break;
case PLAYER_SOURCE.PEACOCK:
const sliderHead = document.body.querySelector(
'.playback-scrubber-bar .rc-slider-handle'
window.postMessage(
JSON.stringify({
watchPartyDirection: 'TO_SCRIPT',
type: 'WATCH_PARTY_SEEK',
status: serverPlayer.status,
time: serverPlayer.time,
})
);
break;
case PLAYER_SOURCE.GOOGLEPLAY:
this.video.element.currentTime = serverPlayer.time;
break;
case PLAYER_SOURCE.CRUNCHYROLL:
window.postMessage(
JSON.stringify({
watchPartyDirection: 'TO_SCRIPT',
type: 'WATCH_PARTY_SEEK',
status: serverPlayer.status,
time: serverPlayer.time,
})
);
sliderHead.style.left =
((serverPlayer.time * 1000) / sliderHead.getAttribute('aria-valueMax')) * 100 + '%';
simulate(sliderHead, 'mousedown');
simulate(sliderHead, 'mouseup');
await this.timeout(500);
break;
default:
break;
@@ -126,9 +138,23 @@ export default class VideoListener {
PLAYER_SOURCE.NETFLIX === this.video.source ||
PLAYER_SOURCE.HBOMAX === this.video.source ||
PLAYER_SOURCE.HBONOW === this.video.source
)
) {
window.postMessage({ type: 'PAUSE', time: serverPlayer.time }, '*');
else await this.video.element.pause();
} else if (
PLAYER_SOURCE.CRUNCHYROLL === this.video.source ||
PLAYER_SOURCE.PEACOCK === this.video.source
) {
window.postMessage(
JSON.stringify({
watchPartyDirection: 'TO_SCRIPT',
type: 'WATCH_PARTY_STATUS',
status: serverPlayer.status,
time: serverPlayer.time,
})
);
} else {
await this.video.element.pause();
}
break;
case PLAYER_STATUS.PLAYING:
if (
@@ -136,20 +162,38 @@ export default class VideoListener {
PLAYER_SOURCE.NETFLIX === this.video.source ||
PLAYER_SOURCE.HBOMAX === this.video.source ||
PLAYER_SOURCE.HBONOW === this.video.source
)
) {
window.postMessage({ type: 'PLAY', time: serverPlayer.time }, '*');
else
} else if (
PLAYER_SOURCE.CRUNCHYROLL === this.video.source ||
PLAYER_SOURCE.PEACOCK === this.video.source
) {
window.postMessage(
JSON.stringify({
watchPartyDirection: 'TO_SCRIPT',
type: 'WATCH_PARTY_STATUS',
status: serverPlayer.status,
time: serverPlayer.time,
})
);
} else {
await this.video.element
.play()
.catch((err) => console.error('WatchParty erred when syncing play.'));
}
break;
default:
break;
}
await this.timeout(500);
// RE-ADD LISTENERS FOR FUTURE SYNC
this.video.element.addEventListener('playing', this.onplay);
this.video.element.addEventListener('pause', this.onpause);
switch (this.video.source) {
case PLAYER_SOURCE.CRUNCHYROLL:
window.addEventListener('watchParty', this.crunchyRollListener);
default:
this.video.element.addEventListener('playing', this.onplay);
this.video.element.addEventListener('pause', this.onpause);
}
}
this.isUpdating = false;
}
@@ -218,6 +262,20 @@ export default class VideoListener {
return peacockshow === '' && peacockepisode === ''
? null
: peacockshow + '\n' + peacockepisode;
case PLAYER_SOURCE.GOOGLEPLAY:
const playerIframe = document.querySelector(
'#yDmH0d > div.llhEMd.bYEzqc.LAhoGb.iWO5td > div > div.LhXUod.XL54pf.J9Nfi.iWO5td > span > div > div > iframe'
);
var googleplayshow = '';
if (playerIframe) {
const titleElement = playerIframe.contentWindow.document.querySelector('.playing-title');
googleplayshow = titleElement ? titleElement.innerText : '';
}
return googleplayshow === '' ? null : googleplayshow;
case PLAYER_SOURCE.CRUNCHYROLL:
const crunchyrollTitle = document.querySelector('#showmedia_about_media');
const crunchyrollshow = crunchyrollTitle ? crunchyrollTitle.innerText : '';
return crunchyrollshow === '' ? null : crunchyrollshow;
default:
break;
}
@@ -236,21 +294,39 @@ export default class VideoListener {
showName: await this.getShowName(),
});
}
async crunchyRollListener(ev) {
if (
ev.detail.watchPartyDirection === 'TO_APP' &&
ev.detail.type === 'WATCH_PARTY_PLAYER_UPDATE'
) {
this.updateStatus({
status: ev.detail.status,
time: ev.detail.time,
showName: await this.getShowName(),
});
}
}
async firstTimeSync(videoElement) {
this.updateStatus({
element: videoElement,
});
// if (videoElement.currentTime > 0 && !videoElement.paused) {
// this.updateStatus({
// time: videoElement.currentTime,
// status: PLAYER_STATUS.PLAYING,
// });
// }
this.updateStatus({
showName: await this.getShowName(),
});
videoElement.addEventListener('playing', this.onplay);
videoElement.addEventListener('pause', this.onpause);
switch (this.video.source) {
case PLAYER_SOURCE.CRUNCHYROLL:
window.postMessage(
JSON.stringify({
watchPartyDirection: 'TO_SCRIPT',
type: 'WATCH_PARTY_ADD_LISTENERS',
})
);
window.addEventListener('watchParty', this.crunchyRollListener);
break;
default:
videoElement.addEventListener('playing', this.onplay);
videoElement.addEventListener('pause', this.onpause);
}
}
async start() {
this.updateStatus({ source: getPlayerSource(), showName: await this.getShowName() });
@@ -271,9 +347,11 @@ export default class VideoListener {
this.video.element
? null
: () => {
var nve = document
.getElementsByClassName('AkiraPlayer')[0]
.getElementsByTagName('video')[0];
var nve = document.getElementsByClassName('AkiraPlayer')[0]
? document
.getElementsByClassName('AkiraPlayer')[0]
.getElementsByTagName('video')[0]
: null;
if (
nve !== null &&
(this.video.element !== nve || this.video.element === null)
@@ -289,9 +367,11 @@ export default class VideoListener {
this.video.element
? null
: () => {
var nve = document
.getElementsByClassName('AkiraPlayer')[0]
.getElementsByTagName('video')[0];
var nve = document.getElementsByClassName('AkiraPlayer')[0]
? document
.getElementsByClassName('AkiraPlayer')[0]
.getElementsByTagName('video')[0]
: null;
if (nve !== null && (this.video.element !== nve || this.video.element === null)) {
this.firstTimeSync(nve);
}
@@ -430,8 +510,12 @@ export default class VideoListener {
}
break;
case PLAYER_SOURCE.PEACOCK:
const peacockInject = `var watchPartyInteraction=function(e){if(e.source===window){const data=JSON.parse(e.data);const playerReactElement=document.querySelector(".video-player-component.playback-component > div");if(data.watchPartyDirection==='TO_SCRIPT'){if(data.type==='WATCH_PARTY_SEEK'){if(playerReactElement&&playerReactElement.player){playerReactElement.player.seek(data.time)}}else if(data.type==='WATCH_PARTY_STATUS'){if(playerReactElement&&playerReactElement.player){if(data.status==='PAUSED')playerReactElement.player.pause();if(data.status==='PLAYING')playerReactElement.player.play()}}}}};window.addEventListener("message",watchPartyInteraction);`;
const peacockInjectScript = document.createElement('script');
peacockInjectScript.text = peacockInject;
document.head.appendChild(peacockInjectScript);
const peacockVideoElement = document.querySelector(
'#mount > div > div > div > div > div > div.video-player-component.playback-component > div > video'
'.video-player-component.playback-component video'
);
if (peacockVideoElement) {
this.firstTimeSync(peacockVideoElement);
@@ -441,7 +525,7 @@ export default class VideoListener {
? null
: () => {
var pve = document.querySelector(
'#mount > div > div > div > div > div > div.video-player-component.playback-component > div > video'
'.video-player-component.playback-component video'
);
if (
pve !== null &&
@@ -455,6 +539,55 @@ export default class VideoListener {
);
}
break;
case PLAYER_SOURCE.GOOGLEPLAY:
const playerIframe = document.querySelector(
'#yDmH0d > div.llhEMd.bYEzqc.LAhoGb.iWO5td > div > div.LhXUod.XL54pf.J9Nfi.iWO5td > span > div > div > iframe'
);
if (playerIframe) {
const googlePlayVideoElement = playerIframe.contentWindow.document.querySelector('video');
if (googlePlayVideoElement) {
this.firstTimeSync(googlePlayVideoElement);
}
} else {
setInterval(
this.video.element
? null
: () => {
const pif = document.querySelector(
'#yDmH0d > div.llhEMd.bYEzqc.LAhoGb.iWO5td > div > div.LhXUod.XL54pf.J9Nfi.iWO5td > span > div > div > iframe'
);
if (pif) {
const gpve = pif.contentWindow.document.querySelector('video');
if (gpve) {
this.firstTimeSync(gpve);
}
}
},
1000
);
}
case PLAYER_SOURCE.CRUNCHYROLL:
const crunchyRollInject = `var sendToWatchPartyApp = function(pS, pT) { var customWatchPartyEvent = new CustomEvent('watchParty', { detail: { watchPartyDirection: 'TO_APP', type: 'WATCH_PARTY_PLAYER_UPDATE', status: pS, time: pT } }); window.dispatchEvent(customWatchPartyEvent); }, watchPartyGetTimeAndSend = function(pS) { if (window.VILOS_PLAYERJS) { window.VILOS_PLAYERJS.getCurrentTime(function(pT) { sendToWatchPartyApp(pS, pT); }); } }, watchPartySendPlaying = function() { watchPartyGetTimeAndSend('PLAYING'); }, watchPartySendPaused = function() { watchPartyGetTimeAndSend('PAUSED'); }, watchPartyInteraction = function(e) { if (e.source === window) { const data = JSON.parse(e.data); if (data.watchPartyDirection === 'TO_SCRIPT') { if (data.type === 'WATCH_PARTY_ADD_LISTENERS') { if (window.VILOS_PLAYERJS) { window.VILOS_PLAYERJS.on('play', watchPartySendPlaying, 'player.js'); window.VILOS_PLAYERJS.on('pause', watchPartySendPaused, 'player.js'); } } else if (data.type === 'WATCH_PARTY_SEEK') { if (window.VILOS_PLAYERJS) { window.VILOS_PLAYERJS.setCurrentTime(data.time); } } else if (data.type === 'WATCH_PARTY_STATUS') { if (window.VILOS_PLAYERJS) { if (data.status === 'PAUSED') window.VILOS_PLAYERJS.pause(); if (data.status === 'PLAYING') window.VILOS_PLAYERJS.play(); } } } } }; window.addEventListener("message", watchPartyInteraction);`;
const crunchyRollInjectScript = document.createElement('script');
crunchyRollInjectScript.text = crunchyRollInject;
document.head.appendChild(crunchyRollInjectScript);
const crunchyrollIframe = document.getElementById('vilos-player');
if (crunchyrollIframe) {
this.firstTimeSync(crunchyrollIframe);
} else {
setInterval(
this.video.element
? null
: () => {
const cif = document.getElementById('vilos-player');
if (cif) {
this.firstTimeSync(cif);
}
},
1000
);
}
break;
case PLAYER_SOURCE.LOCAL:
const localVideoElement = document.getElementsByTagName('video')[0];
if (localVideoElement) {
@@ -466,9 +599,15 @@ export default class VideoListener {
}
}
end() {
if (this.video.element) {
this.video.element.removeEventListener('playing', this.onplay);
this.video.element.removeEventListener('pause', this.onpause);
switch (this.video.source) {
case PLAYER_SOURCE.CRUNCHYROLL:
window.removeEventListener('watchParty', this.crunchyRollListener);
break;
default:
if (this.video.element) {
this.video.element.removeEventListener('playing', this.onplay);
this.video.element.removeEventListener('pause', this.onpause);
}
}
}
}
+393 -71
View File
@@ -5,7 +5,7 @@ Proprietary and confidential
Written by Ayushya Amitabh <admin@chromewatchparty.com>, 2020
https://www.chromewatchparty.com/copyright
=========================================================================
ALL CONTENT ON IN WATCH PARTY'S CHROME EXTENSION APP, UNLESS OTHERWISE
ALL CONTENT ON WATCH PARTY'S WEBSITE OR IN WATCH PARTY'S CHROME EXTENSION APP, UNLESS OTHERWISE
CITED ARE THE SOLE INTELECTUAL PROPERTY OF AYUSHYA AMITABH, AND MAY NOT BE
COPIED, EDITED, OR REPRODUCED WITHOUT PERMISSION.
@@ -17,8 +17,8 @@ Hulu logo is a trademark and intellectual property of Hulu, LLC.
HBO Now and HBO Max logos are trademark and intellectual property of WarnerMedia Direct,LLC.
************************************************************************/
:root {
--party-width: 400px;
--neg-party-width: -400px;
--party-width: 425px;
--neg-party-width: -425px;
--body-width: calc(100% - var(--party-width));
}
body {
@@ -35,7 +35,7 @@ body.visible {
body.visible .watch-party-fab {
right: var(--party-width) !important;
transform: translateX(0);
top: 1.5rem;
top: 22.5px !important;
border-top-right-radius: 0;
border-bottom-right-radius: 0;
background-color: #424242 !important;
@@ -47,12 +47,35 @@ body.visible .party-notification-container {
max-width: var(--party-width);
}
#party-width-dragger {
display: none;
position: absolute;
padding: 15px 3.5px;
right: var(--party-width);
top: 50%;
background: #2d2d2d;
cursor: ew-resize;
font-size: 16px;
width: fit-content;
user-select: none;
transition: all ease-in-out 0.3s;
}
body.visible #party-width-dragger {
display: block;
}
body.hbo.visible #party-width-dragger {
display: none !important;
}
body.netflix.visible .AkiraPlayer, /* NETFLIX */
body.hulu.visible .Player__container,/* HULU */
body.amazon-prime.visible #dv-web-player.dv-player-fullscreen, /* AMAZON PRIME */
body.disney.visible #hudson-wrapper, /* DISNEY */
body.youtube.visible #content.ytd-app, /* YOUTUBE */
body.youtube.visible #masthead-container /* YOUTUBE HEADER */ {
body.youtube.visible #masthead-container, /* YOUTUBE HEADER */
body.googleplay.visible div.llhEMd.bYEzqc.LAhoGb.iWO5td /* GOOGLE PLAY MOVIES */ {
max-width: var(--body-width) !important;
}
/* HULU MINIMIZED VIDEO */
@@ -130,16 +153,79 @@ body.peacock.visible .playback-header__container {
position: relative;
max-width: 100%;
}
/* GOOGLE PLAY STYLING */
body.googleplay.visible {
-webkit-font-smoothing: initial !important;
}
body.googleplay.visible div.llhEMd.bYEzqc.LAhoGb.iWO5td > div.mjANdc.eEPege {
padding: 0 !important;
}
body.googleplay.visible
div.llhEMd.bYEzqc.LAhoGb.iWO5td
> div.mjANdc.eEPege
> div.LhXUod.XL54pf.J9Nfi.iWO5td,
body.googleplay.visible
div.llhEMd.bYEzqc.LAhoGb.iWO5td
> div
> div.LhXUod.XL54pf.J9Nfi.iWO5td
> span
> div
> div
> iframe {
min-height: 100%;
width: 100% !important;
height: 100% !important;
}
body.googleplay.visible
div.llhEMd.bYEzqc.LAhoGb.iWO5td
> div
> div.LhXUod.XL54pf.J9Nfi.iWO5td
> span
> div,
body.googleplay.visible
div.llhEMd.bYEzqc.LAhoGb.iWO5td
> div
> div.LhXUod.XL54pf.J9Nfi.iWO5td
> span
> div
> div {
height: 100%;
}
/* CRUNCHYROLL STYLING */
body.crunchyroll.visible #party-routes-wrapper input {
border: initial !important;
}
body.crunchyroll.visible header {
z-index: 0 !important;
}
body.crunchyroll.visible #showmedia_video {
position: fixed;
z-index: 99999;
width: calc(100% - var(--party-width));
background: black;
top: 0;
left: 0;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
}
body.crunchyroll.visible #showmedia_video_box {
width: 100% !important;
height: 100% !important;
}
body.crunchyroll.visible #showmedia_video_player {
display: flex !important;
align-items: center !important;
}
body.crunchyroll.visible #vilos-player {
max-height: 42vw !important;
}
/*
FULL SCREEN BUTTON REMOVER
*/
body.netflix
.touchable.PlayerControls--control-element.nfp-button-control.default-control-button.button-nfplayerFullscreen,
body.hulu .controls__view-mode-button-normal-icon.player-svgicon,
body.disney .control-icon-btn.fullscreen-icon,
body.amazon .fyysciv.f1lobqlo.fk0j80b {
display: none;
body.hulu #party_wrapper *,
body.peacock #party_wrapper *,
body.disney #party_wrapper * {
box-sizing: inherit;
}
#party-loading {
@@ -179,13 +265,13 @@ body.amazon .fyysciv.f1lobqlo.fk0j80b {
background-color: rgb(27, 27, 27);
bottom: 0;
right: var(--neg-party-width);
transition: all ease-in-out 0.3s;
width: var(--party-width);
height: 100%;
color: white;
display: flex;
flex-direction: column;
z-index: 9999;
transition: all ease-in-out 0.3s !important;
}
#party-routes-wrapper.visible {
right: 0;
@@ -216,11 +302,11 @@ body.amazon .fyysciv.f1lobqlo.fk0j80b {
.watch-party-fab {
z-index: 9999;
position: fixed !important;
bottom: 1rem !important;
top: calc(100% - 75px) !important;
right: -2rem !important;
border: 1px solid white;
background-color: black !important;
/* background-image: url('/android-icon-192x192.png'); */
transition: all ease-in-out 0.3s !important;
background-image: url('chrome-extension://__MSG_@@extension_id__/android-icon-192x192.png');
background-position: center;
background-repeat: no-repeat;
@@ -243,10 +329,13 @@ body.amazon .fyysciv.f1lobqlo.fk0j80b {
display: flex;
flex-direction: column;
height: 100%;
max-height: 100% !important;
overflow-y: auto !important;
background: #424242;
padding: 0;
}
.party-create-container .brand-title {
font-size: 18px !important;
padding: 36px;
letter-spacing: 3.5px !important;
}
@@ -260,6 +349,7 @@ body.amazon .fyysciv.f1lobqlo.fk0j80b {
display: flex;
justify-content: center;
background: #424242;
box-shadow: none !important;
}
.create-input-card * {
font-size: 12px !important;
@@ -271,9 +361,6 @@ body.amazon .fyysciv.f1lobqlo.fk0j80b {
.party-create-container .create-input-card .create-pin {
margin-bottom: 1rem;
}
.party-create-container .create-input-card {
background-color: #2d2d2d;
}
.create-bottom-card {
background-color: #202020;
padding: 30px 15px;
@@ -288,12 +375,12 @@ body.amazon .fyysciv.f1lobqlo.fk0j80b {
.create-bottom-card a:hover {
color: #c0f300;
}
.thread-container {
.thread-wrapper {
max-height: 100%;
flex: 1;
overflow-y: auto;
margin-bottom: 1rem;
padding: 0 15px;
padding: 0;
margin-top: 1rem;
}
.room-title {
@@ -303,17 +390,6 @@ body.amazon .fyysciv.f1lobqlo.fk0j80b {
padding-right: 1rem !important;
font-size: 18px !important;
}
.thread-container::-webkit-scrollbar {
width: 5px;
}
.thread-container::-webkit-scrollbar-track {
background-color: rgb(90, 90, 90);
border-radius: 1rem;
}
.thread-container::-webkit-scrollbar-thumb {
background-color: rgb(180, 180, 180);
border-radius: 13px;
}
textarea::-webkit-scrollbar {
display: none;
}
@@ -430,40 +506,6 @@ textarea::-webkit-scrollbar {
color: inherit !important;
font-size: 10px !important;
}
.giphy-carousel::before {
position: relative;
bottom: 90px;
margin-left: 40px;
content: url('./Giphy.png');
}
.giphy-carousel {
margin-top: 1rem;
min-height: fit-content;
padding-bottom: 3rem;
}
.giphy-carousel img {
border-radius: 1rem;
transform: scale(0.85);
transition: all ease-in-out 0.2s;
opacity: 0.7;
}
.giphy-carousel img:hover {
opacity: 1;
transform: scale(0.9);
}
.giphy-carousel::-webkit-scrollbar {
width: 5px;
height: 12px;
}
.giphy-carousel::-webkit-scrollbar-track {
background-color: rgb(90, 90, 90);
}
.giphy-carousel::-webkit-scrollbar-thumb {
background-color: #c0f300;
border-radius: 13px;
}
.message-send svg {
transform: scale(0.55);
transition: all ease 0.5s !important;
@@ -478,6 +520,7 @@ textarea::-webkit-scrollbar {
display: flex;
align-items: center;
margin: 0 15px 15px 15px;
margin-bottom: 0px;
border-radius: 26px;
}
.text-phone-panel .icon-button {
@@ -487,18 +530,24 @@ textarea::-webkit-scrollbar {
}
.text-phone-panel .gif-button.gif-close {
padding: 12px 0 !important;
max-height: 100%;
margin-left: -5px;
}
.text-phone-panel .gif-button {
border-radius: 0 !important;
height: 1.75em !important;
padding: 0 6px;
border-radius: 26px !important;
border-top-right-radius: 0 !important;
border-bottom-right-radius: 0 !important;
}
.text-phone-panel .gif-button svg {
width: 1.75em !important;
height: 1.75em !important;
width: 32px !important;
height: 32px !important;
}
.text-phone-panel .gif-button.gif-close svg {
width: 2em !important;
height: 1.5em !important;
height: 25px !important;
width: 25px !important;
}
.MuiTooltip-popper {
@@ -533,6 +582,18 @@ body.amazon-prime .notification-switch .MuiSwitch-thumb {
.alerts-container {
margin: 5px 0;
position: sticky;
top: 0;
padding: 5px 0;
z-index: 999 !important;
background: #1b1b1b;
}
.alert-card {
border: none !important;
border-radius: 26px !important;
margin: 0px 10px !important;
margin-bottom: 5px !important;
}
.alert-card-content {
@@ -548,6 +609,10 @@ body.amazon-prime .notification-switch .MuiSwitch-thumb {
align-items: center;
font-size: 11px;
text-transform: uppercase;
flex: 1;
text-align: center !important;
justify-content: center;
max-width: 90% !important;
}
#mic-permission-overlay {
@@ -586,5 +651,262 @@ body.amazon-prime .notification-switch .MuiSwitch-thumb {
font-size: 14px !important;
letter-spacing: 1.75px !important;
font-weight: 300 !important;
color: black !important;
border-radius: 0 !important;
}
.MuiAutocomplete-popper {
z-index: 99999 !important;
}
.MuiAutocomplete-popper * {
color: white !important;
}
.admin-only-switch {
margin-bottom: 20px !important;
transition: all ease-in 0.3s !important;
height: 70px !important;
margin-top: 0 !important;
overflow: hidden;
opacity: 1;
}
.admin-only-switch.admin-only-switch-hidden {
height: 0 !important;
opacity: 0;
}
.replying-to-card {
display: flex !important;
padding: 15px 20px 500px 20px !important;
border: none !important;
border-top-left-radius: 26px !important;
border-top-right-radius: 26px !important;
margin-bottom: -554px !important;
opacity: 0 !important;
transition: all ease-in-out 0.3s !important;
}
.replying-to-card.replying-to-card-up {
margin-bottom: -480px !important;
opacity: 1 !important;
}
.replying-to-content {
flex: 1;
overflow: hidden;
}
.replying-to-content img {
max-height: 75px;
border-radius: 26px;
}
.replying-to-content p {
font-size: 11px;
letter-spacing: 1.2px;
font-weight: 100;
overflow: hidden;
position: relative;
white-space: nowrap;
max-width: 100%;
text-overflow: ellipsis;
}
.replying-to-card button {
align-self: flex-start;
}
.giphy-wrapper.giphy-wrapper-down {
padding-top: 30px;
margin-bottom: -350px;
}
.giphy-wrapper.giphy-wrapper-down .giphy-list-wrapper {
overflow: hidden;
}
.giphy-wrapper {
padding-top: 0px;
transition: all ease-in-out 0.3s;
position: relative;
box-sizing: content-box !important;
}
.giphy-list-wrapper {
max-height: 350px !important;
overflow-y: auto !important;
overflow-x: hidden;
padding: 15px;
}
.giphy-list-wrapper > div {
box-sizing: border-box !important;
}
.thread-wrapper::-webkit-scrollbar,
.giphy-list-wrapper::-webkit-scrollbar {
width: 5px;
background-color: transparent;
transition: all ease-in-out 0.3s;
}
.thread-wrapper::-webkit-scrollbar:hover,
.giphy-list-wrapper::-webkit-scrollbar:hover {
background-color: #424242;
}
.thread-wrapper::-webkit-scrollbar-track,
.giphy-list-wrapper::-webkit-scrollbar-track {
background-color: transparent;
}
.thread-wrapper::-webkit-scrollbar-thumb,
.giphy-list-wrapper::-webkit-scrollbar-thumb {
background-color: #bef300ad;
border-radius: 26px !important;
}
.thread-wrapper::-webkit-scrollbar-thumb:hover,
.giphy-list-wrapper::-webkit-scrollbar-thumb:hover {
background-color: #c0f300;
}
.giphy-search-form {
height: 0;
width: 0;
overflow: hidden;
}
button.giphy-list-item {
display: inline-block;
position: relative !important;
height: fit-content;
vertical-align: top;
top: unset !important;
overflow: hidden;
background: unset !important;
left: unset !important;
cursor: pointer;
outline: none !important;
border: none !important;
opacity: 0.3;
transition: all ease-in-out 0.3s;
width: 50%;
max-width: 200px;
max-height: 100px !important;
}
.giphy-list-item:hover {
opacity: 1;
max-height: none !important;
}
.giphy-list-item img {
height: 100%;
margin: 5px;
width: 100%;
max-width: 100%;
}
.giphy-wrapper > div:nth-child(3) {
position: absolute;
bottom: 0;
color: black;
padding: 5px 15px;
padding-right: 25px;
border-top-right-radius: 26px;
padding-top: 15px;
border-top: 1px solid gray;
border-right: 1px solid gray;
backdrop-filter: blur(10px);
background-color: rgba(255, 255, 255, 0.2);
transition: all ease-in-out 0.3s;
}
.giphy-wrapper > div:nth-child(3):hover {
background-color: white;
}
.giphy-wrapper > div:nth-child(3) > img {
filter: invert(1);
vertical-align: unset;
max-height: 20px;
filter: drop-shadow(0px 0px 0px black);
mix-blend-mode: darken;
border: none;
}
.giphy-wrapper > .giphy-list-wrapper > div > div:nth-child(1) {
width: 100% !important;
}
.create-settings-card {
margin: 0 !important;
}
.create-settings-card .create-settings-title {
margin-left: 15px !important;
}
.create-settings-card .MuiExpansionPanelDetails-root {
flex-direction: column !important;
}
.create-settings-card button {
font-size: 12px !important;
margin-top: 25px !important;
text-align: center !important;
}
.alerts-stepper-container {
position: relative;
width: 100%;
}
.alerts-stepper-card {
margin-bottom: 15px;
border-radius: 26px !important;
background: #202020 !important;
}
.alerts-stepper-card span {
font-size: 11px !important;
}
.alerts-stepper-card p {
font-size: 14px !important;
}
.alerts-stepper-card .MuiCardActions-root {
padding-left: 0 !important;
padding-right: 0 !important;
padding-bottom: 0 !important;
}
.alerts-stepper-card .MuiCardActions-root a {
flex: 1;
border-top-left-radius: 0 !important;
border-top-right-radius: 0 !important;
border-bottom-left-radius: 26px !important;
border-bottom-right-radius: 26px !important;
padding: 10px;
color: #c0f300 !important;
}
.alerts-stepper-controls {
position: relative !important;
background: transparent !important;
}
.alerts-stepper-controls .MuiMobileStepper-dot.MuiMobileStepper-dotActive {
background-color: #c0f300 !important;
}
.alerts-stepper-controls button {
margin-top: 0 !important;
padding: 5px !important;
}
.party-controls-users-volume {
min-width: 150px !important;
}
.party-controls-users-volume .MuiGrid-root.MuiGrid-item {
display: flex !important;
justify-content: center !important;
align-items: center !important;
}
+8 -9
View File
@@ -5,16 +5,9 @@ Proprietary and confidential
Written by Ayushya Amitabh <admin@chromewatchparty.com>, 2020
https://www.chromewatchparty.com/copyright
=========================================================================
ALL CONTENT ON IN WATCH PARTY'S CHROME EXTENSION APP, UNLESS OTHERWISE
ALL CONTENT ON WATCH PARTY'S WEBSITE OR IN WATCH PARTY'S CHROME EXTENSION APP, UNLESS OTHERWISE
CITED ARE THE SOLE INTELECTUAL PROPERTY OF AYUSHYA AMITABH, AND MAY NOT BE
COPIED, EDITED, OR REPRODUCED WITHOUT PERMISSION.
Netflix logo is a trademark and intellectual property of Netflix, Inc.
Disney+ logo is a trademark and intellectual property of The Walt Disney Company
Prime logo is a trademark and intellectual property of Amazon.com, Inc.
YouTube logo is a trademark and intellectual property of Google, Inc.
Hulu logo is a trademark and intellectual property of Hulu, LLC.
HBO Now and HBO Max logos are trademark and intellectual property of WarnerMedia Direct,LLC.
************************************************************************/
import React from 'react';
import ReactDOM from 'react-dom';
@@ -86,7 +79,7 @@ switch (getPlayerSource()) {
break;
case PLAYER_SOURCE.HBONOW:
case PLAYER_SOURCE.HBOMAX:
const hboClickInject = `var hboInputFix=function(ev){if(ev.target.tagName==='INPUT'||ev.target.tagName==='TEXTAREA'){ev.target.focus();}ev.stopImmediatePropagation();ev.stopPropagation();};document.addEventListener('keydown',hboInputFix,{capture:true});var hboClickFix=function(ev){if(document.querySelector('body.visible')){if(ev.clientX > window.innerWidth - 400){if(ev.target.tagName==='INPUT'||ev.target.tagName==='TEXTAREA'){ev.target.focus();}ev.preventDefault();ev.stopPropagation();ev.stopImmediatePropagation();}}};document.addEventListener("mousedown",hboClickFix,{capture:true});`;
const hboClickInject = `var hboInputFix=function(ev){if(ev.target.tagName==='INPUT'||ev.target.tagName==='TEXTAREA'){ev.target.focus();}ev.stopImmediatePropagation();ev.stopPropagation();};document.addEventListener('keydown',hboInputFix,{capture:true});var hboClickFix=function(ev){if(document.querySelector('body.visible')){if(ev.clientX > window.innerWidth - (parseInt(document.documentElement.style.getPropertyValue('--party-width').slice(0,-2)) + 50) ){if(ev.target.tagName==='INPUT'||ev.target.tagName==='TEXTAREA'){ev.target.focus();}ev.preventDefault();ev.stopPropagation();ev.stopImmediatePropagation();}}};document.addEventListener("mousedown",hboClickFix,{capture:true});`;
const hboClickInjectScript = document.createElement('script');
hboClickInjectScript.text = hboClickInject;
document.head.appendChild(hboClickInjectScript);
@@ -95,6 +88,12 @@ switch (getPlayerSource()) {
case PLAYER_SOURCE.PEACOCK:
document.body.classList.add('peacock');
break;
case PLAYER_SOURCE.GOOGLEPLAY:
document.body.classList.add('googleplay');
break;
case PLAYER_SOURCE.CRUNCHYROLL:
document.body.classList.add('crunchyroll');
break;
case PLAYER_SOURCE.LOCAL:
document.body.classList.add('local');
break;
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 85 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 75 KiB

+11 -1
View File
@@ -1,13 +1,21 @@
const FunctionModules = require('./modules/index');
const FunctionModules2 = require('./modules2/index');
const Webhooks = require('./webhooks/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);
// VERSION 1
const app = express();
app.use(cors({ origin: true }));
app.use(FunctionModules.authenticator);
app.get('/validator', FunctionModules.tokenValidator);
app.get('/validator/:partyName', FunctionModules.tokenValidator);
app.get('/refresher/:token', FunctionModules.tokenRefresher);
app.post('/create', FunctionModules.createRoom);
app.post('/join', FunctionModules.joinRoom);
@@ -16,6 +24,7 @@ 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);
// VERSION 2
const app2 = express();
@@ -34,4 +43,5 @@ app2.delete('/delete/:rid', FunctionModules2.deleteRoom);
module.exports = {
v1: app,
v2: app2,
webhooks,
};
+2 -5
View File
@@ -1,4 +1,3 @@
const FunctionModules = require('./modules/index');
const functions = require('firebase-functions');
const secret = require('./FirebaseSecret.json');
const admin = require('firebase-admin');
@@ -15,7 +14,5 @@ admin.initializeApp(FIREBASE_OPTIONS);
exports.widgets = functions.https.onRequest(REST.v1);
// VERSION 2
exports.widgets2 = functions.https.onRequest(REST.v2);
exports.scheduledCleanUp = functions.pubsub
.schedule('every 60 minutes')
.onRun(FunctionModules.scheduledCleanUp);
// WEBHOOKS
exports.webhooks = functions.https.onRequest(REST.webhooks);
+1 -1
View File
@@ -22,7 +22,7 @@ module.exports = (req, res, next) => {
bodydata.uid = user.uid;
req.body = JSON.stringify(bodydata);
}
if (req.method === 'GET' || req.method === 'DELETE') {
if (req.method === 'GET' || req.method === 'DELETE' || req.method === 'PUT') {
req.body = JSON.stringify({ uid: user.uid });
}
return next();
-32
View File
@@ -1,32 +0,0 @@
const admin = require('firebase-admin');
module.exports = async (roomId) => {
admin
.firestore()
.collection('rooms')
.doc(roomId)
.collection('messages')
.listDocuments()
.then((messages) => messages.forEach((message) => message.delete()))
.catch((err) => {
console.error(`Failed to run auto clean up (delete room messages) @ ${roomId}`, err);
});
admin
.firestore()
.collection('rooms')
.doc(roomId)
.collection('users')
.listDocuments()
.then((users) => users.forEach((user) => user.delete()))
.catch((err) => {
console.error(`Failed to run auto clean up (delete room users) @ ${roomId}`, err);
});
admin
.firestore()
.collection('rooms')
.doc(roomId)
.delete()
.catch((err) => {
console.error(`Failed to run auto clean up (delete room) @ ${roomId}`, err);
});
};
+1 -17
View File
@@ -24,6 +24,7 @@ module.exports = async (req, res) => {
createdAt: new Date(),
updatedAt: new Date(),
adminUid: bodydata.uid,
adminOnly: bodydata.adminOnly,
})
.catch((err) => {
res.status(500).send({ message: 'Unable to start party.' });
@@ -46,23 +47,6 @@ module.exports = async (req, res) => {
err
);
});
await admin
.firestore()
.collection('rooms')
.doc(bodydata.partyName)
.collection('messages')
.add({
uid: '<system>',
name: '',
text: `Voice calls are unavailable temporarily. Voice calls will be back within 12 hours.`,
time: new Date(),
})
.catch((err) => {
console.error(
`Failed to add system voice call message : \n\t Room: ${bodydata.partyName}`,
err
);
});
await admin
.firestore()
.collection('rooms')
-1
View File
@@ -2,7 +2,6 @@ const admin = require('firebase-admin');
module.exports = async (req, res) => {
console.log('Requested Delete - Room - ', req.params.rid);
console.log(req.body);
const isAdmin = await admin
.firestore()
.collection('rooms')
+2 -4
View File
@@ -1,6 +1,4 @@
const authenticator = require('./authenticator');
const cleanUpRoom = require('./cleanUpRoom');
const scheduledCleanUp = require('./scheduledCleanUp');
const createRoom = require('./createRoom');
const joinRoom = require('./joinRoom');
const sendMessage = require('./sendMessage');
@@ -11,6 +9,7 @@ const updatePlayer = require('./updatePlayer');
const tokenValidator = require('./tokenValidator');
const tokenRefresher = require('./tokenRefresher');
const updateVoxeet = require('./updateVoxeet');
const updateAdminOnly = require('./updateAdminOnly');
module.exports = {
authenticator,
@@ -20,10 +19,9 @@ module.exports = {
updatePlayer,
leaveRoom,
deleteRoom,
scheduledCleanUp,
cleanUpRoom,
timeDiff,
tokenValidator,
tokenRefresher,
updateVoxeet,
updateAdminOnly,
};
+1 -2
View File
@@ -1,5 +1,4 @@
const admin = require('firebase-admin');
const { firestore } = require('firebase-admin');
module.exports = async (req, res) => {
const bodydata = JSON.parse(req.body);
@@ -137,7 +136,7 @@ module.exports = async (req, res) => {
.update({
player: {
showName: null,
source: null,
source: bodydata.source,
status: 'NOT_PLAYING',
time: 0,
updatedBy: bodydata.uid,
+2 -1
View File
@@ -1,7 +1,7 @@
const admin = require('firebase-admin');
module.exports = async (req, res) => {
const { name, text, uid, roomId, type } = JSON.parse(req.body);
const { name, text, uid, roomId, type, replyTo } = JSON.parse(req.body);
const roomExists = await admin
.firestore()
@@ -21,6 +21,7 @@ module.exports = async (req, res) => {
time: new Date(),
uid,
type: type || '',
replyTo: replyTo || null,
})
.catch((err) => {
console.error(`Failed to send message to party : \n\t Room: ${roomId} \n\t ${uid}`, err);
+17 -18
View File
@@ -1,21 +1,20 @@
const VoxeetSecret = require('../VoxeetSecret.json');
module.exports = async (req, res) => {
res.status(403).send({ message: 'Voice calls are unavailable temporarily.' });
// const { key, secret } = VoxeetSecret;
// const credentials = btoa(encodeURI(key) + ':' + encodeURI(secret));
// const headers = {
// 'Content-Type': 'application/json',
// 'Cache-Control': 'no-cache',
// Authorization: 'Basic ' + credentials,
// };
// const oauth = await fetch(VoxeetSecret.refreshURL, {
// method: 'POST',
// headers,
// body: JSON.stringify({
// refresh_token: req.params.token,
// }),
// })
// .then((oauthResponse) => oauthResponse.json())
// .catch((err) => res.status(500).send(err));
// if (oauth) res.status(200).send(oauth);
const { key, secret } = VoxeetSecret;
const credentials = btoa(encodeURI(key) + ':' + encodeURI(secret));
const headers = {
'Content-Type': 'application/json',
'Cache-Control': 'no-cache',
Authorization: 'Basic ' + credentials,
};
const oauth = await fetch(VoxeetSecret.refreshURL, {
method: 'POST',
headers,
body: JSON.stringify({
refresh_token: req.params.token,
}),
})
.then((oauthResponse) => oauthResponse.json())
.catch((err) => res.status(500).send(err));
if (oauth) res.status(200).send(oauth);
};
+60 -18
View File
@@ -1,22 +1,64 @@
const VoxeetSecret = require('../VoxeetSecret.json');
const admin = require('firebase-admin');
const fetch = require('node-fetch');
module.exports = async (req, res) => {
res.status(403).send({ message: 'Voice calls are unavailable temporarily.' });
// const { key, secret } = VoxeetSecret;
// const credentials = Buffer.from(encodeURI(key) + ':' + encodeURI(secret)).toString('base64');
// const headers = {
// 'Content-Type': 'application/json',
// 'Cache-Control': 'no-cache',
// Authorization: 'Basic ' + credentials,
// };
// const oauth = await fetch(VoxeetSecret.validateURL, {
// method: 'POST',
// headers,
// body: JSON.stringify({
// grant_type: 'client_credentials',
// }),
// })
// .then((oauthResponse) => oauthResponse.json())
// .catch((err) => res.status(500).send(err));
// if (oauth) res.status(200).send(oauth);
const bodyData = JSON.parse(req.body);
const { key, secret } = VoxeetSecret;
const credentials = Buffer.from(encodeURI(key) + ':' + encodeURI(secret)).toString('base64');
const headers = {
'Content-Type': 'application/json',
'Cache-Control': 'no-cache',
Authorization: 'Basic ' + credentials,
};
const oauth = await fetch(VoxeetSecret.validateURL, {
method: 'POST',
headers,
body: JSON.stringify({
grant_type: 'client_credentials',
}),
}).then((oauthResponse) => oauthResponse.json())
.catch((err) => res.status(500).send({err, message: 'Unable to generate token for call'}));
const [exists, conferencePending, conference, adminOnly, adminUid] = await admin
.firestore()
.collection('rooms')
.doc(req.params.partyName)
.get()
.then(snap => {
const data = snap.exists ? snap.data() : {};
return [snap.exists, data.conferencePending, data.conference, data.adminOnly, data.adminUid];
});
if (exists === true) {
if (conference === undefined || conference === null) {
if (conferencePending === undefined || conferencePending === null) {
if (!adminOnly || (adminOnly && (adminUid === bodyData.uid)) ) {
await admin
.firestore()
.collection('rooms')
.doc(req.params.partyName)
.update({
conferencePending: bodyData.uid
});
await admin
.firestore()
.collection('rooms')
.doc(req.params.partyName)
.collection('messages')
.add({
uid: '<system>',
name: '',
text: 'A call session is being created (will be available soon).',
time: new Date(),
});
}
}
}
if (oauth) res.status(200).send(oauth);
} else {
res.status(400).send({ message: 'Unable to get party for call.' });
}
return;
};
+36
View File
@@ -0,0 +1,36 @@
const admin = require('firebase-admin');
module.exports = async (req, res) => {
const bodydata = JSON.parse(req.body);
const isAdmin = await admin
.firestore()
.collection('rooms')
.doc(req.params.rid)
.collection('users')
.doc(bodydata.uid)
.get()
.then((v) => {
if (v.exists) {
return v.data().admin;
}
return false;
})
.catch((err) => {
console.err('Failed to get party to update adminOnly', err);
return res.status(404).send({ message: 'Failed to get party.' });
});
if (!isAdmin) {
console.log('[UNAUTHORIZED] Requested Admin Mode Update - Room - ', req.params.rid);
res.status(401).send({ message: 'Not authorized to update admin only mode for this party.' });
return;
}
const adminOnly = req.params.adminOnly === 'true';
await admin.firestore().collection('rooms').doc(req.params.rid).update({
adminOnly
}).catch((err) => {
console.err('Failed to update adminOnly for party', err);
res.status(409).send({ message: 'Failed to update party.' });
});
res.status(200).send({ message: 'Updated admin only mode for party' });
return;
};
+15 -41
View File
@@ -2,18 +2,7 @@ const admin = require('firebase-admin');
module.exports = async (req, res) => {
const { player, roomId, uid, showChanged, userName } = JSON.parse(req.body);
// const statusString =
// player.status === 'PLAYING'
// ? 'started playing from'
// : player.status === 'PAUSED'
// ? 'paused at'
// : player.status === 'NOT_PLAYING'
// ? 'stopped at'
// : 'is at';
// const timeString =
// player.time > 3600
// ? new Date(player.time * 1000).toISOString().substr(11, 8)
// : new Date(player.time * 1000).toISOString().substr(14, 5);
if (!roomId) return res.status(204).send({ message: 'Party name not sent.' });
const [roomExists, roomData] = await admin
.firestore()
.collection('rooms')
@@ -30,18 +19,20 @@ module.exports = async (req, res) => {
.get()
.then((userSnap) => userSnap.exists);
if (userExists) {
if (showChanged === true) {
await admin
.firestore()
.collection('rooms')
.doc(roomId)
.collection('messages')
.add({
uid: '<system>',
name: '',
text: `${userName} changed \n${player.showName ? 'to ' + player.showName : ''}`,
time: new Date(),
});
if (showChanged === true && player.showName) {
if (roomData.player.showName !== player.showName) {
await admin
.firestore()
.collection('rooms')
.doc(roomId)
.collection('messages')
.add({
uid: '<system>',
name: '',
text: `${userName} changed \n${player.showName ? 'to ' + player.showName : ''}`,
time: new Date(),
});
}
}
if (roomData.status !== player.status) {
await admin
@@ -55,23 +46,6 @@ module.exports = async (req, res) => {
console.error(`Failed to update player : \n\t Room: ${roomId} \n\t ${uid}`, err);
res.status(500).send({ message: "Uh oh party could't be synced", err });
});
// await admin
// .firestore()
// .collection('rooms')
// .doc(roomId)
// .collection('messages')
// .add({
// uid: '<system>',
// name: '',
// text: `${userName} ${statusString} ${timeString}`,
// time: new Date(),
// })
// .catch((err) => {
// console.error(
// `Failed to add system update player message : \n\t Room: ${roomId} \n\t ${uid}`,
// err
// );
// });
return res.status(200).send({ message: 'Synced party player' });
} else {
return res.status(208).send({ message: 'Player already up to date' });
+77 -35
View File
@@ -1,47 +1,89 @@
const admin = require('firebase-admin');
const admin = require("firebase-admin");
module.exports = async (req, res) => {
const { conference, partyName, partyPin, userName } = JSON.parse(req.body);
const { conference, partyName, partyPin, userName, uid } = JSON.parse(
req.body
);
// GET ROOM PIN
const roomPin = await admin
const [exists, roomPin, conferencePending, partyConference] = await admin
.firestore()
.collection('rooms')
.collection("rooms")
.doc(partyName)
.get()
.then((roomSnap) => (roomSnap.exists ? roomSnap.data().pin : null));
.then((roomSnap) => {
const data = roomSnap.exists ? roomSnap.data() : {};
return [
roomSnap.exists,
data.pin,
data.conferencePending,
data.conference,
];
});
// CHECK IF ROOM EXISTS
if (roomPin) {
// CHECK FOR CORRECT PIN
if (roomPin === partyPin) {
// UPDATE CONFERENCE DETAILS
await admin
.firestore()
.collection('rooms')
.doc(partyName)
.update({ conference })
.catch((err) =>
res.status(500).send({ message: 'Unable to update the call details', error: err })
);
// ADD SYSTEM MESSAGE
await admin
.firestore()
.collection('rooms')
.doc(partyName)
.collection('messages')
.add({
uid: '<system>',
name: '',
text: `${userName} started the call.`,
time: new Date(),
})
.catch((err) => console.error('Failed to add update conference message', err));
res.status(200).send({ message: 'Started the conference' });
} else {
// WRONG ROOM PIN
return res.status(401).send({ message: 'Wrong party pin, try rejoining' });
if (exists === true) {
if (roomPin) {
// CHECK FOR CORRECT PIN
if (roomPin === partyPin) {
if (conferencePending) {
if (conferencePending === uid) {
// UPDATE CONFERENCE DETAILS
await admin
.firestore()
.collection("rooms")
.doc(partyName)
.update({ conference })
.catch((err) =>
res.status(500).send({
message: "Unable to update the call details",
error: err,
})
);
// REMOVE PENDING STATUS
await admin
.firestore()
.collection("rooms")
.doc(partyName)
.update({
conferencePending: admin.firestore.FieldValue.delete(),
})
.catch((err) =>
console.error("Failed to remove pending status", err)
);
// ADD SYSTEM MESSAGE
await admin
.firestore()
.collection("rooms")
.doc(partyName)
.collection("messages")
.add({
uid: "<system>",
name: "",
text: `${userName} started the call, everyone can join now.`,
time: new Date(),
})
.catch((err) =>
console.error("Failed to add update conference message", err)
);
res.status(200).send({ message: "Started the conference" });
} else {
res.status(409).send({
message: "Looks like someone is already starting a call",
});
}
} else if (partyConference) {
res.status(409).send({
message: "Call already exists",
conference: partyConference,
});
}
} else {
// WRONG ROOM PIN
res.status(401).send({ message: "Wrong party pin, try rejoining" });
}
}
} else {
// ROOM DOESN'T EXIST
return res.status(404).send({ message: 'Party Not Found' });
res.status(404).send({ message: "Party Not Found" });
}
return;
};
+52 -17
View File
@@ -1,21 +1,27 @@
const admin = require('firebase-admin');
const admin = require("firebase-admin");
module.exports = async (req, res) => {
const bodydata = JSON.parse(req.body);
console.log('Requested Create - UID - ', bodydata.uid);
if (!bodydata.userName) res.status(400).send({ message: 'Missing user name' });
if (!bodydata.partyName) res.status(400).send({ message: 'Missing party name' });
if (!bodydata.partyPin) res.status(400).send({ message: 'Missing party pin' });
console.log("Requested Create - UID - ", bodydata.uid);
if (!bodydata.userName)
res.status(400).send({ message: "Missing user name" });
if (!bodydata.partyName)
res.status(400).send({ message: "Missing party name" });
if (!bodydata.partyPin)
res.status(400).send({ message: "Missing party pin" });
const roomNameTaken = await admin
.firestore()
.collection('rooms')
.doc(bodydata.partyName.replace('/', '\\'))
.collection("rooms")
.doc(bodydata.partyName.replace("/", "\\"))
.get()
.then((docSnap) => docSnap.exists);
if (roomNameTaken) return res.status(409).send({ message: 'That party name is already taken' });
if (roomNameTaken)
return res
.status(409)
.send({ message: "That party name is already taken" });
await admin
.firestore()
.collection('rooms')
.collection("rooms")
.doc(bodydata.partyName)
.set({
name: bodydata.partyName,
@@ -26,17 +32,17 @@ module.exports = async (req, res) => {
adminUid: bodydata.uid,
})
.catch((err) => {
res.status(500).send({ message: 'Unable to start party.' });
res.status(500).send({ message: "Unable to start party." });
console.error(err);
});
await admin
.firestore()
.collection('rooms')
.collection("rooms")
.doc(bodydata.partyName)
.collection('messages')
.collection("messages")
.add({
uid: '<system>',
name: '',
uid: "<system>",
name: "",
text: `${bodydata.userName} started the party.`,
time: new Date(),
})
@@ -48,9 +54,9 @@ module.exports = async (req, res) => {
});
await admin
.firestore()
.collection('rooms')
.collection("rooms")
.doc(bodydata.partyName)
.collection('users')
.collection("users")
.doc(bodydata.uid)
.set({
admin: true,
@@ -62,5 +68,34 @@ module.exports = async (req, res) => {
err
);
});
res.status(201).send({ roomId: bodydata.partyName, message: 'Started a party!' });
admin
.firestore()
.collection("rooms")
.doc(bodydata.partyName)
.collection("messages")
.add({
uid: "<system>",
name: "",
text: `Big update coming on Monday (Aug. 10). Watch Party will be unavailable during the afternoon as we upgrade our services. See what's new at whatsnew.chromewatchparty.com`,
time: new Date(),
});
if (player.source === "peacocktv.") {
admin
.firestore()
.collection("rooms")
.doc(bodydata.partyName)
.collection("messages")
.add({
uid: "<system>",
name: "",
text: `Sync may not work flawlessly with PeacockTV till Monday's update. Please stick with us as we address this issue.`,
time: new Date(),
});
}
res
.status(201)
.send({ roomId: bodydata.partyName, message: "Started a party!" });
};
+1 -1
View File
@@ -136,7 +136,7 @@ module.exports = async (req, res) => {
.update({
player: {
showName: null,
source: roomSource,
source: bodydata.source,
status: 'NOT_PLAYING',
time: 0,
updatedBy: bodydata.uid,
+13 -11
View File
@@ -32,17 +32,19 @@ module.exports = async (req, res) => {
.then((userSnap) => userSnap.exists);
if (userExists) {
if (showChanged === true && player.showName) {
await admin
.firestore()
.collection('rooms')
.doc(roomId)
.collection('messages')
.add({
uid: '<system>',
name: '',
text: `${userName} changed \n${player.showName ? 'to ' + player.showName : ''}`,
time: new Date(),
});
if (player.showName !== roomData.player.showName) {
await admin
.firestore()
.collection('rooms')
.doc(roomId)
.collection('messages')
.add({
uid: '<system>',
name: '',
text: `${userName} changed \n${player.showName ? 'to ' + player.showName : ''}`,
time: new Date(),
});
}
}
if (roomData.status !== player.status) {
await admin
+4 -4
View File
@@ -1,6 +1,6 @@
{
"name": "watch-party-api",
"version": "1.2.1",
"version": "1.6.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@@ -1909,9 +1909,9 @@
}
},
"lodash": {
"version": "4.17.15",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz",
"integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A=="
"version": "4.17.19",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz",
"integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ=="
},
"lodash.at": {
"version": "4.6.0",
+5
View File
@@ -0,0 +1,5 @@
module.exports = async (req, res) => {
const { question, answer } = JSON.parse(req.body);
console.info(`[RECEIVED]\tWEBHOOKS/ASK\tQ:${question}\tA:${answer}`);
res.status(200).send({ message: 'Got answer.' });
};
+46
View File
@@ -0,0 +1,46 @@
const admin = require("firebase-admin");
module.exports = async (partyName) => {
// DELETE MESSAGES
const partyMessagesRef = admin
.firestore()
.collection("rooms")
.doc(partyName)
.collection("messages");
const partyMessagesQuery = partyMessagesRef.orderBy("time").limit(200);
await deleteCollection(partyMessagesQuery);
// DELETE USERS
const partyUsersRef = admin
.firestore()
.collection("rooms")
.doc(partyName)
.collection("users");
const partyUsersQuery = partyUsersRef.orderBy("time").limit(200);
await deleteCollection(partyUsersQuery);
// DELETE PARTY DOC
await admin.firestore().collection("rooms").doc(partyName).delete();
};
async function deleteCollection(query) {
const db = admin.firestore();
return new Promise((resolve, reject) => {
deleteQueryBatch(db, query, resolve).catch(reject);
});
}
async function deleteQueryBatch(db, query, resolve) {
const snapshot = await query.get();
const batchSize = snapshot.size;
if (batchSize === 0) {
resolve();
return;
}
const batch = admin.firestore().batch();
snapshot.docs.forEach((doc) => {
batch.delete(doc.ref);
});
await batch.commit();
process.nextTick(() => {
deleteQueryBatch(db, query, resolve);
});
}
+70
View File
@@ -0,0 +1,70 @@
const admin = require("firebase-admin");
module.exports = async (req, res) => {
console.info(
`[START]\tWEBHOOKS/Conference End\tID:${req.body.conference.confId}\tParty:${req.body.conference.confAlias}`
);
// MAKE SURE CORRECT EVENT TYPE
if (req.body.eventType === "Conference.Ended") {
// MAKE SURE IT'S NOT A DUPLICATE FROM CREATE CONFLICT
if (req.body.participants.length > 0) {
// MAKE SURE ELIGIBLE FOR DELETE
if (req.body.confAlias.startsWith("AUTODELETE_")) {
const partyName = req.body.confAlias.replace(/AUTODELETE_/, "");
const existingConference = await admin
.firestore()
.collection("rooms")
.doc(partyName)
.get()
.then((snap) => (snap.exists ? snap.data().conference : null));
// MAKE SURE EXISTS AND HAS NOT BEEN REPLACED
if (
existingConference &&
existingConference._id === req.body.conference.confId
) {
// REMOVE CONFERENCE DETAILS
await admin
.firestore()
.collection("rooms")
.doc(partyName)
.update({ conference: admin.firestore.FieldValue.delete() })
.then(() => {
console.info(
`[DONE]\tWEBHOOKS/Conference End\tID:${req.body.conference.confId}\tParty:${req.body.conference.confAlias}`
);
return;
})
.catch((err) => {
console.error(
`[ERROR]\tWEBHOOKS/Conference End/Firebase Error\tID:${
req.body.conference.confId
}\tParty:${
req.body.conference.confAlias
}\tError:${JSON.stringify(err)}`
);
});
} else {
console.info(
`[DONE]\tWEBHOOKS/Conference End/Conference Was Already Replaced\tID:${req.body.conference.confId}\tParty:${req.body.conference.confAlias}`
);
}
res.status(200).send({ message: "Processed conference end" });
} else {
console.info(
`[DONE]\tWEBHOOKS/Conference End/Not Eligible For Autodelete\tID:${req.body.conference.confId}\tParty:${req.body.conference.confAlias}`
);
res.status(200).send({ message: "Processed conference end" });
}
} else {
console.info(
`[DONE]\tWEBHOOKS/Conference End/End Of Duplicate Conference\tID:${req.body.conference.confId}\tParty:${req.body.conference.confAlias}`
);
}
} else {
console.error(
`[ERROR]\tWEBHOOKS/Conference End/Wrong Event Type\tID:${req.body.conference.confId}\tParty:${req.body.conference.confAlias}`
);
res.status(400).send({ message: "Wrong event type" });
}
return;
};
+9
View File
@@ -0,0 +1,9 @@
const conferenceEnd = require('./conferenceEnd');
const scheduledCleanUp = require('./scheduledCleanUp');
const ask = require('./ask');
module.exports = {
conferenceEnd,
scheduledCleanUp,
ask,
};
@@ -1,5 +1,5 @@
const admin = require('firebase-admin');
const cleanUpRoom = require('./cleanUpRoom');
const admin = require("firebase-admin");
const cleanUpRoom = require("./cleanUpRoom");
const timeDiff = (ds1, ds2) => {
const d1 = new Date(ds1);
@@ -9,11 +9,12 @@ const timeDiff = (ds1, ds2) => {
return Math.abs(Math.round(diff));
};
module.exports = async () => {
module.exports = async (req, res) => {
const startedTime = new Date();
res.status(200).send({ message: "Started processing" });
const roomList = await admin
.firestore()
.collection('rooms')
.collection("rooms")
.listDocuments()
.then((rL) => rL)
.catch((err) => {
@@ -29,11 +30,11 @@ module.exports = async () => {
if (
// MORE THAN 24 HOURS AND NOT IN USE
(timeDiff(roomSnap.createTime.toDate(), startedTime) >= 24 &&
timeDiff(roomSnap.updateTime.toDate(), startedTime) >= 12) ||
timeDiff(roomSnap.updateTime.toDate(), startedTime) >= 4) ||
// LESS THAN 24 HOURS BUT NOT IN USE
timeDiff(roomSnap.updateTime.toDate(), startedTime) >= 12
) {
await cleanUpRoom(roomSnap.id);
cleanUpRoom(roomSnap.id);
console.info(`Deleted room ${roomSnap.id} from auto cleanup`);
}
return;
@@ -45,4 +46,5 @@ module.exports = async () => {
);
});
});
return;
};
+5
View File
@@ -4,6 +4,11 @@ Copyright (C) Ayushya Amitabh - All Rights Reserved
Unauthorized copying of this file, via any medium is strictly prohibited
Proprietary and confidential
Written by Ayushya Amitabh <admin@chromewatchparty.com>, 2020
https://www.chromewatchparty.com/copyright
=========================================================================
ALL CONTENT ON WATCH PARTY'S WEBSITE OR IN WATCH PARTY'S CHROME EXTENSION APP, UNLESS OTHERWISE
CITED ARE THE SOLE INTELECTUAL PROPERTY OF AYUSHYA AMITABH, AND MAY NOT BE
COPIED, EDITED, OR REPRODUCED WITHOUT PERMISSION.
------------------------------------------------------------------------->
<html>
<head>
+5
View File
@@ -4,6 +4,11 @@ Copyright (C) Ayushya Amitabh - All Rights Reserved
Unauthorized copying of this file, via any medium is strictly prohibited
Proprietary and confidential
Written by Ayushya Amitabh <admin@chromewatchparty.com>, 2020
https://www.chromewatchparty.com/copyright
=========================================================================
ALL CONTENT ON WATCH PARTY'S WEBSITE OR IN WATCH PARTY'S CHROME EXTENSION APP, UNLESS OTHERWISE
CITED ARE THE SOLE INTELECTUAL PROPERTY OF AYUSHYA AMITABH, AND MAY NOT BE
COPIED, EDITED, OR REPRODUCED WITHOUT PERMISSION.
------------------------------------------------------------------------->
<html>
<head>
+15
View File
@@ -4,12 +4,27 @@ Copyright (C) Ayushya Amitabh - All Rights Reserved
Unauthorized copying of this file, via any medium is strictly prohibited
Proprietary and confidential
Written by Ayushya Amitabh <admin@chromewatchparty.com>, 2020
https://www.chromewatchparty.com/copyright
=========================================================================
ALL CONTENT ON WATCH PARTY'S WEBSITE OR IN WATCH PARTY'S CHROME EXTENSION APP, UNLESS OTHERWISE
CITED ARE THE SOLE INTELECTUAL PROPERTY OF AYUSHYA AMITABH, AND MAY NOT BE
COPIED, EDITED, OR REPRODUCED WITHOUT PERMISSION.
------------------------------------------------------------------------->
<html>
<head>
<meta http-equiv="refresh" content="0; URL='https://request.chromewatchparty.com'" />
<title>Watch Party | Beta</title>
</head>
<!-- Global site tag (gtag.js) - Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-87498806-2">
</script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'UA-87498806-2');
</script>
<body>
Redirecting...
</body>
+115 -33
View File
@@ -4,48 +4,119 @@ Copyright (C) Ayushya Amitabh - All Rights Reserved
Unauthorized copying of this file, via any medium is strictly prohibited
Proprietary and confidential
Written by Ayushya Amitabh <admin@chromewatchparty.com>, 2020
https://www.chromewatchparty.com/copyright
=========================================================================
ALL CONTENT ON THIS WEBSITE, UNLESS OTHERWISE CITED ARE THE SOLE
INTELECTUAL PROPERTY OF AYUSHYA AMITABH, AND MAY NOT BE COPIED, EDITED,
OR REPRODUCED WITHOUT PERMISSION.
Netflix logo is a trademark and intellectual property of Netflix, Inc.
Disney+ logo is a trademark and intellectual property of The Walt Disney Company
Prime logo is a trademark and intellectual property of Amazon.com, Inc.
YouTube logo is a trademark and intellectual property of Google, Inc.
Hulu logo is a trademark and intellectual property of Hulu, LLC.
HBO Now and HBO Max logos are trademark and intellectual property of WarnerMedia Direct,LLC.
Peacock TV logo is a trademark and intellectual property of Peacock TV & NBC Universal.
ALL CONTENT ON WATCH PARTY'S WEBSITE OR IN WATCH PARTY'S CHROME EXTENSION APP, UNLESS OTHERWISE
CITED ARE THE SOLE INTELECTUAL PROPERTY OF AYUSHYA AMITABH, AND MAY NOT BE
COPIED, EDITED, OR REPRODUCED WITHOUT PERMISSION.
------------------------------------------------------------------------->
<html>
<head>
<link rel="apple-touch-icon" sizes="57x57" href="../icons/apple-icon-57x57.png" />
<link rel="apple-touch-icon" sizes="60x60" href="../icons/apple-icon-60x60.png" />
<link rel="apple-touch-icon" sizes="72x72" href="../icons/apple-icon-72x72.png" />
<link rel="apple-touch-icon" sizes="76x76" href="../icons/apple-icon-76x76.png" />
<link rel="apple-touch-icon" sizes="114x114" href="../icons/apple-icon-114x114.png" />
<link rel="apple-touch-icon" sizes="120x120" href="../icons/apple-icon-120x120.png" />
<link rel="apple-touch-icon" sizes="144x144" href="../icons/apple-icon-144x144.png" />
<link rel="apple-touch-icon" sizes="152x152" href="../icons/apple-icon-152x152.png" />
<link rel="apple-touch-icon" sizes="180x180" href="../icons/apple-icon-180x180.png" />
<link rel="icon" type="image/png" sizes="192x192" href="../icons/android-icon-192x192.png" />
<link rel="icon" type="image/png" sizes="32x32" href="../icons/favicon-32x32.png" />
<link rel="icon" type="image/png" sizes="96x96" href="../icons/favicon-96x96.png" />
<link rel="icon" type="image/png" sizes="16x16" href="../icons/favicon-16x16.png" />
<link
rel="apple-touch-icon"
sizes="57x57"
href="../icons/apple-icon-57x57.png"
/>
<link
rel="apple-touch-icon"
sizes="60x60"
href="../icons/apple-icon-60x60.png"
/>
<link
rel="apple-touch-icon"
sizes="72x72"
href="../icons/apple-icon-72x72.png"
/>
<link
rel="apple-touch-icon"
sizes="76x76"
href="../icons/apple-icon-76x76.png"
/>
<link
rel="apple-touch-icon"
sizes="114x114"
href="../icons/apple-icon-114x114.png"
/>
<link
rel="apple-touch-icon"
sizes="120x120"
href="../icons/apple-icon-120x120.png"
/>
<link
rel="apple-touch-icon"
sizes="144x144"
href="../icons/apple-icon-144x144.png"
/>
<link
rel="apple-touch-icon"
sizes="152x152"
href="../icons/apple-icon-152x152.png"
/>
<link
rel="apple-touch-icon"
sizes="180x180"
href="../icons/apple-icon-180x180.png"
/>
<link
rel="icon"
type="image/png"
sizes="192x192"
href="../icons/android-icon-192x192.png"
/>
<link
rel="icon"
type="image/png"
sizes="32x32"
href="../icons/favicon-32x32.png"
/>
<link
rel="icon"
type="image/png"
sizes="96x96"
href="../icons/favicon-96x96.png"
/>
<link
rel="icon"
type="image/png"
sizes="16x16"
href="../icons/favicon-16x16.png"
/>
<link rel="icon" href="../icons/favicon.ico" />
<link rel="manifest" href="../manifest.json" />
<meta name="msapplication-TileImage" content="../icons/ms-icon-144x144.png" />
<meta
name="msapplication-TileImage"
content="../icons/ms-icon-144x144.png"
/>
<link rel="stylesheet" type="text/css" href="../font.css" />
<link rel="stylesheet" type="text/css" href="../styles/index.css" />
<script src="../scripts/Stickied.js"></script>
<title>Watch Party | Changelog</title>
</head>
<!-- Global site tag (gtag.js) - Google Analytics -->
<script
async
src="https://www.googletagmanager.com/gtag/js?id=UA-87498806-2"
></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag() {
dataLayer.push(arguments);
}
gtag("js", new Date());
gtag("config", "UA-87498806-2");
</script>
<body class="copyright">
<div class="landing">
<div class="logo-circle"></div>
<div class="logo-circle"></div>
<div class="logo-circle"></div>
<h1 data-font="serif" class="logo"><a href="../">WATCH PARTY</a> | CHANGELOG</h1>
<h1 data-font="serif" class="logo duplicate">
<a href="../">WATCH PARTY</a> | CHANGELOG
</h1>
<h1 data-font="serif" class="logo">
<a href="../">WATCH PARTY</a> | CHANGELOG
</h1>
<h5 data-font="sans" class="logo-tag">
WATCH YOUR FAVORITES,<br />WITH YOUR FAVORITE PEOPLE
</h5>
@@ -61,26 +132,37 @@ Peacock TV logo is a trademark and intellectual property of Peacock TV & NBC Uni
<div
class="supported-logo"
style="background-image: url(../media/versions/160.png);"
data-upcoming-support="Work In Progress"
data-upcoming-support="See Changes"
></div>
</a>
</div>
</div>
<footer data-font="sans">
<p class="title built-with">
<a href="../copyright/">COPYRIGHT</a> &copy; 2020 AYUSHYA AMITABH. ALL RIGHTS RESERVED.
<a href="../copyright/">COPYRIGHT</a> &copy; 2020 AYUSHYA AMITABH. ALL
RIGHTS RESERVED.
</p>
<p class="title built-with">
BUILT WITH &#x1F49A; FOR <a href="https://www.google.com/chrome/">GOOGLE CHROME</a> USING
BUILT WITH &#x1F49A; FOR
<a href="https://www.google.com/chrome/">GOOGLE CHROME</a> USING
<a href="https://firebase.google.com/">FIREBASE</a>,
<a href="https://material-ui.com/">MATERIAL-UI</a>,
<a href="https://reactjs.org/">REACT JS</a>, <a href="https://nodejs.org/en/">NODE JS</a>,
<a href="https://giphy.com/">GIPHY</a>, & <a href="https://dolby.io/">DOLBY VOXEET</a>.
<a href="https://reactjs.org/">REACT JS</a>,
<a href="https://nodejs.org/en/">NODE JS</a>,
<a href="https://giphy.com/">GIPHY</a>, &
<a href="https://dolby.io/">DOLBY VOXEET</a>.
</p>
<a data-font="sans-bold" href="https://paypal.me/Ayushya" class="link-button">
<a
data-font="sans-bold"
href="https://paypal.me/Ayushya"
class="link-button"
>
Support Watch Party
</a>
<a data-font="sans-bold" href="https://request.chromewatchparty.com" class="link-button"
<a
data-font="sans-bold"
href="https://request.chromewatchparty.com"
class="link-button"
>Get Chrome Extension</a
>
<a
+165 -44
View File
@@ -4,81 +4,190 @@ Copyright (C) Ayushya Amitabh - All Rights Reserved
Unauthorized copying of this file, via any medium is strictly prohibited
Proprietary and confidential
Written by Ayushya Amitabh <admin@chromewatchparty.com>, 2020
https://www.chromewatchparty.com/copyright
=========================================================================
ALL CONTENT ON THIS WEBSITE, UNLESS OTHERWISE CITED ARE THE SOLE
INTELECTUAL PROPERTY OF AYUSHYA AMITABH, AND MAY NOT BE COPIED, EDITED,
OR REPRODUCED WITHOUT PERMISSION.
Netflix logo is a trademark and intellectual property of Netflix, Inc.
Disney+ logo is a trademark and intellectual property of The Walt Disney Company
Prime logo is a trademark and intellectual property of Amazon.com, Inc.
YouTube logo is a trademark and intellectual property of Google, Inc.
Hulu logo is a trademark and intellectual property of Hulu, LLC.
HBO Now and HBO Max logos are trademark and intellectual property of WarnerMedia Direct,LLC.
Peacock TV logo is a trademark and intellectual property of Peacock TV & NBC Universal.
ALL CONTENT ON WATCH PARTY'S WEBSITE OR IN WATCH PARTY'S CHROME EXTENSION APP, UNLESS OTHERWISE
CITED ARE THE SOLE INTELECTUAL PROPERTY OF AYUSHYA AMITABH, AND MAY NOT BE
COPIED, EDITED, OR REPRODUCED WITHOUT PERMISSION.
------------------------------------------------------------------------->
<html>
<head>
<link rel="apple-touch-icon" sizes="57x57" href="../../icons/apple-icon-57x57.png" />
<link rel="apple-touch-icon" sizes="60x60" href="../../icons/apple-icon-60x60.png" />
<link rel="apple-touch-icon" sizes="72x72" href="../../icons/apple-icon-72x72.png" />
<link rel="apple-touch-icon" sizes="76x76" href="../../icons/apple-icon-76x76.png" />
<link rel="apple-touch-icon" sizes="114x114" href="../../icons/apple-icon-114x114.png" />
<link rel="apple-touch-icon" sizes="120x120" href="../../icons/apple-icon-120x120.png" />
<link rel="apple-touch-icon" sizes="144x144" href="../../icons/apple-icon-144x144.png" />
<link rel="apple-touch-icon" sizes="152x152" href="../../icons/apple-icon-152x152.png" />
<link rel="apple-touch-icon" sizes="180x180" href="../../icons/apple-icon-180x180.png" />
<link rel="icon" type="image/png" sizes="192x192" href="../../icons/android-icon-192x192.png" />
<link rel="icon" type="image/png" sizes="32x32" href="../../icons/favicon-32x32.png" />
<link rel="icon" type="image/png" sizes="96x96" href="../../icons/favicon-96x96.png" />
<link rel="icon" type="image/png" sizes="16x16" href="../../icons/favicon-16x16.png" />
<link
rel="apple-touch-icon"
sizes="57x57"
href="../../icons/apple-icon-57x57.png"
/>
<link
rel="apple-touch-icon"
sizes="60x60"
href="../../icons/apple-icon-60x60.png"
/>
<link
rel="apple-touch-icon"
sizes="72x72"
href="../../icons/apple-icon-72x72.png"
/>
<link
rel="apple-touch-icon"
sizes="76x76"
href="../../icons/apple-icon-76x76.png"
/>
<link
rel="apple-touch-icon"
sizes="114x114"
href="../../icons/apple-icon-114x114.png"
/>
<link
rel="apple-touch-icon"
sizes="120x120"
href="../../icons/apple-icon-120x120.png"
/>
<link
rel="apple-touch-icon"
sizes="144x144"
href="../../icons/apple-icon-144x144.png"
/>
<link
rel="apple-touch-icon"
sizes="152x152"
href="../../icons/apple-icon-152x152.png"
/>
<link
rel="apple-touch-icon"
sizes="180x180"
href="../../icons/apple-icon-180x180.png"
/>
<link
rel="icon"
type="image/png"
sizes="192x192"
href="../../icons/android-icon-192x192.png"
/>
<link
rel="icon"
type="image/png"
sizes="32x32"
href="../../icons/favicon-32x32.png"
/>
<link
rel="icon"
type="image/png"
sizes="96x96"
href="../../icons/favicon-96x96.png"
/>
<link
rel="icon"
type="image/png"
sizes="16x16"
href="../../icons/favicon-16x16.png"
/>
<link rel="icon" href="../../icons/favicon.ico" />
<link rel="manifest" href="../../manifest.json" />
<meta name="msapplication-TileImage" content="../../icons/ms-icon-144x144.png" />
<meta
name="msapplication-TileImage"
content="../../icons/ms-icon-144x144.png"
/>
<link rel="stylesheet" type="text/css" href="../../font.css" />
<link rel="stylesheet" type="text/css" href="../../styles/index.css" />
<script src="../../scripts/Stickied.js"></script>
<title>Watch Party | V1.5.2</title>
</head>
<!-- Global site tag (gtag.js) - Google Analytics -->
<script
async
src="https://www.googletagmanager.com/gtag/js?id=UA-87498806-2"
></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag() {
dataLayer.push(arguments);
}
gtag("js", new Date());
gtag("config", "UA-87498806-2");
</script>
<body class="copyright welcome">
<div class="landing">
<div class="logo-circle"></div>
<div class="logo-circle"></div>
<div class="logo-circle"></div>
<h1 data-font="serif" class="logo"><a href="../../">WATCH PARTY</a> | V1.5.2</h1>
<h1 data-font="serif" class="logo duplicate">
<a href="../../">WATCH PARTY</a> | V1.5.2
</h1>
<h1 data-font="serif" class="logo">
<a href="../../">WATCH PARTY</a> | V1.5.2
</h1>
<h5 data-font="sans" class="logo-tag">
WATCH YOUR FAVORITES,<br />WITH YOUR FAVORITE PEOPLE
</h5>
</div>
<div class="features">
<p data-font="sans-bold" class="title subtitle">CHANGES MADE IN FEATURES</p>
<p data-font="sans-bold" class="title subtitle">
CHANGES MADE IN FEATURES
</p>
<div class="feature-card">
<div class="feature-icon" id="sync"></div>
<div class="feature-icon" id="sync">
<video
loop
muted
autoplay
src="../../media/features/Sync.mp4"
></video>
</div>
<p data-font="sans" class="feature-text">
Fixed Netflix bug to autodetect player.
</p>
</div>
<div class="feature-card">
<div class="feature-icon" id="phone"></div>
<div class="feature-icon" id="phone">
<video
loop
muted
autoplay
src="../../media/features/Phone.mp4"
></video>
</div>
<p data-font="sans" class="feature-text">
Added mic permission overlay, and add proper loading icon to show a connecting call.
Added mic permission overlay, and add proper loading icon to show a
connecting call.
</p>
</div>
<div class="feature-card">
<div class="feature-icon" id="chat"></div>
<div class="feature-icon" id="chat">
<video
loop
muted
autoplay
src="../../media/features/Chat.mp4"
></video>
</div>
<p data-font="sans" class="feature-text">
Gif scrollbar is now easier to grab and scroll.
</p>
</div>
<div class="feature-card">
<div class="feature-icon" id="hideable"></div>
<div class="feature-icon" id="hideable">
<video
loop
muted
autoplay
src="../../media/features/Hideable.mp4"
></video>
</div>
<p data-font="sans" class="feature-text">
Fullscreen support, use the button in the sidebar to have access to Watch Party in
fullscreen
Fullscreen support, use the button in the sidebar to have access to
Watch Party in fullscreen
</p>
</div>
<div class="feature-card">
<div class="feature-icon" id="custom"></div>
<div class="feature-icon" id="custom">
<video
loop
muted
autoplay
src="../../media/features/Custom.mp4"
></video>
</div>
<p data-font="sans" class="feature-text">
Added auto rejoin (as long you return within 1 minute)
</p>
@@ -120,13 +229,14 @@ Peacock TV logo is a trademark and intellectual property of Peacock TV & NBC Uni
</div>
<div class="tip">
<p data-font="sans-bold">
> Netflix player update (fixes issue when player was not detected if video started before
party)
> Netflix player update (fixes issue when player was not detected if
video started before party)
</p>
</div>
<div class="tip">
<p data-font="sans-bold">
> Room migrate fix (allows party creators to move their parties to new sites)
> Room migrate fix (allows party creators to move their parties to new
sites)
</p>
</div>
<div class="tip">
@@ -149,19 +259,30 @@ Peacock TV logo is a trademark and intellectual property of Peacock TV & NBC Uni
</div>
<footer data-font="sans">
<p class="title built-with">
<a href="../../copyright/">COPYRIGHT</a> &copy; 2020 AYUSHYA AMITABH. ALL RIGHTS RESERVED.
<a href="../../copyright/">COPYRIGHT</a> &copy; 2020 AYUSHYA AMITABH.
ALL RIGHTS RESERVED.
</p>
<p class="title built-with">
BUILT WITH &#x1F49A; FOR <a href="https://www.google.com/chrome/">GOOGLE CHROME</a> USING
BUILT WITH &#x1F49A; FOR
<a href="https://www.google.com/chrome/">GOOGLE CHROME</a> USING
<a href="https://firebase.google.com/">FIREBASE</a>,
<a href="https://material-ui.com/">MATERIAL-UI</a>,
<a href="https://reactjs.org/">REACT JS</a>, <a href="https://nodejs.org/en/">NODE JS</a>,
<a href="https://giphy.com/">GIPHY</a>, & <a href="https://dolby.io/">DOLBY VOXEET</a>.
<a href="https://reactjs.org/">REACT JS</a>,
<a href="https://nodejs.org/en/">NODE JS</a>,
<a href="https://giphy.com/">GIPHY</a>, &
<a href="https://dolby.io/">DOLBY VOXEET</a>.
</p>
<a data-font="sans-bold" href="https://paypal.me/Ayushya" class="link-button">
<a
data-font="sans-bold"
href="https://paypal.me/Ayushya"
class="link-button"
>
Support Watch Party
</a>
<a data-font="sans-bold" href="https://request.chromewatchparty.com" class="link-button"
<a
data-font="sans-bold"
href="https://request.chromewatchparty.com"
class="link-button"
>Get Chrome Extension</a
>
<a
+253 -40
View File
@@ -4,83 +4,285 @@ Copyright (C) Ayushya Amitabh - All Rights Reserved
Unauthorized copying of this file, via any medium is strictly prohibited
Proprietary and confidential
Written by Ayushya Amitabh <admin@chromewatchparty.com>, 2020
https://www.chromewatchparty.com/copyright
=========================================================================
ALL CONTENT ON THIS WEBSITE, UNLESS OTHERWISE CITED ARE THE SOLE
INTELECTUAL PROPERTY OF AYUSHYA AMITABH, AND MAY NOT BE COPIED, EDITED,
OR REPRODUCED WITHOUT PERMISSION.
Netflix logo is a trademark and intellectual property of Netflix, Inc.
Disney+ logo is a trademark and intellectual property of The Walt Disney Company
Prime logo is a trademark and intellectual property of Amazon.com, Inc.
YouTube logo is a trademark and intellectual property of Google, Inc.
Hulu logo is a trademark and intellectual property of Hulu, LLC.
HBO Now and HBO Max logos are trademark and intellectual property of WarnerMedia Direct,LLC.
Peacock TV logo is a trademark and intellectual property of Peacock TV & NBC Universal.
ALL CONTENT ON WATCH PARTY'S WEBSITE OR IN WATCH PARTY'S CHROME EXTENSION APP, UNLESS OTHERWISE
CITED ARE THE SOLE INTELECTUAL PROPERTY OF AYUSHYA AMITABH, AND MAY NOT BE
COPIED, EDITED, OR REPRODUCED WITHOUT PERMISSION.
------------------------------------------------------------------------->
<html>
<head>
<link rel="apple-touch-icon" sizes="57x57" href="../../icons/apple-icon-57x57.png" />
<link rel="apple-touch-icon" sizes="60x60" href="../../icons/apple-icon-60x60.png" />
<link rel="apple-touch-icon" sizes="72x72" href="../../icons/apple-icon-72x72.png" />
<link rel="apple-touch-icon" sizes="76x76" href="../../icons/apple-icon-76x76.png" />
<link rel="apple-touch-icon" sizes="114x114" href="../../icons/apple-icon-114x114.png" />
<link rel="apple-touch-icon" sizes="120x120" href="../../icons/apple-icon-120x120.png" />
<link rel="apple-touch-icon" sizes="144x144" href="../../icons/apple-icon-144x144.png" />
<link rel="apple-touch-icon" sizes="152x152" href="../../icons/apple-icon-152x152.png" />
<link rel="apple-touch-icon" sizes="180x180" href="../../icons/apple-icon-180x180.png" />
<link rel="icon" type="image/png" sizes="192x192" href="../../icons/android-icon-192x192.png" />
<link rel="icon" type="image/png" sizes="32x32" href="../../icons/favicon-32x32.png" />
<link rel="icon" type="image/png" sizes="96x96" href="../../icons/favicon-96x96.png" />
<link rel="icon" type="image/png" sizes="16x16" href="../../icons/favicon-16x16.png" />
<link
rel="apple-touch-icon"
sizes="57x57"
href="../../icons/apple-icon-57x57.png"
/>
<link
rel="apple-touch-icon"
sizes="60x60"
href="../../icons/apple-icon-60x60.png"
/>
<link
rel="apple-touch-icon"
sizes="72x72"
href="../../icons/apple-icon-72x72.png"
/>
<link
rel="apple-touch-icon"
sizes="76x76"
href="../../icons/apple-icon-76x76.png"
/>
<link
rel="apple-touch-icon"
sizes="114x114"
href="../../icons/apple-icon-114x114.png"
/>
<link
rel="apple-touch-icon"
sizes="120x120"
href="../../icons/apple-icon-120x120.png"
/>
<link
rel="apple-touch-icon"
sizes="144x144"
href="../../icons/apple-icon-144x144.png"
/>
<link
rel="apple-touch-icon"
sizes="152x152"
href="../../icons/apple-icon-152x152.png"
/>
<link
rel="apple-touch-icon"
sizes="180x180"
href="../../icons/apple-icon-180x180.png"
/>
<link
rel="icon"
type="image/png"
sizes="192x192"
href="../../icons/android-icon-192x192.png"
/>
<link
rel="icon"
type="image/png"
sizes="32x32"
href="../../icons/favicon-32x32.png"
/>
<link
rel="icon"
type="image/png"
sizes="96x96"
href="../../icons/favicon-96x96.png"
/>
<link
rel="icon"
type="image/png"
sizes="16x16"
href="../../icons/favicon-16x16.png"
/>
<link rel="icon" href="../../icons/favicon.ico" />
<link rel="manifest" href="../../manifest.json" />
<meta name="msapplication-TileImage" content="../../icons/ms-icon-144x144.png" />
<meta
name="msapplication-TileImage"
content="../../icons/ms-icon-144x144.png"
/>
<link rel="stylesheet" type="text/css" href="../../font.css" />
<link rel="stylesheet" type="text/css" href="../../styles/index.css" />
<script src="../../scripts/Stickied.js"></script>
<title>Watch Party | V1.6.0</title>
</head>
<!-- Global site tag (gtag.js) - Google Analytics -->
<script
async
src="https://www.googletagmanager.com/gtag/js?id=UA-87498806-2"
></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag() {
dataLayer.push(arguments);
}
gtag("js", new Date());
gtag("config", "UA-87498806-2");
</script>
<body class="copyright welcome">
<div class="landing">
<div class="logo-circle"></div>
<div class="logo-circle"></div>
<div class="logo-circle"></div>
<h1 data-font="serif" class="logo"><a href="../../">WATCH PARTY</a> | V1.6.0</h1>
<h1 data-font="serif" class="logo duplicate">
<a href="../../">WATCH PARTY</a> | V1.6.0
</h1>
<h1 data-font="serif" class="logo">
<a href="../../">WATCH PARTY</a> | V1.6.0
</h1>
<h5 data-font="sans" class="logo-tag">
WATCH YOUR FAVORITES,<br />WITH YOUR FAVORITE PEOPLE
</h5>
</div>
<div class="features">
<p data-font="sans-bold" class="title subtitle">PLANNED CHANGES</p>
<p data-font="sans-bold" class="title subtitle">
CHANGES MADE IN FEATURES
</p>
<div class="feature-card">
<div class="feature-icon" id="sync"></div>
<div class="feature-icon" id="sync">
<video
loop
muted
autoplay
src="../../media/features/Sync.mp4"
></video>
</div>
<p data-font="sans" class="feature-text">
Add support for Crunchyroll
Add support for Crunchyroll & Google Play Movies & TV
</p>
</div>
<div class="feature-card">
<div class="feature-icon">
<video
loop
muted
autoplay
src="../../media/features/Phone.mp4"
></video>
</div>
<p data-font="sans" class="feature-text">
Anyone can start calls now, and calls are more reliable.
</p>
</div>
<div class="feature-card">
<div class="feature-icon">
<video
loop
muted
autoplay
src="../../media/features/Chat.mp4"
></video>
</div>
<p data-font="sans" class="feature-text">
Revamped chat and gif UI, now with replies
</p>
</div>
<div class="feature-card">
<div class="feature-icon">
<video
loop
muted
autoplay
src="../../media/features/Hideable.mp4"
></video>
</div>
<p data-font="sans" class="feature-text">
Sidebar is now resizable and has better fullscreen support
</p>
</div>
<div class="feature-card">
<div class="feature-icon">
<video
loop
muted
autoplay
src="../../media/features/Custom.mp4"
></video>
</div>
<p data-font="sans" class="feature-text">
Autocomplete and cross-site party name and pin history
</p>
</div>
</div>
<div id="tips" class="landing">
<p data-font="sans-bold" class="title">
PLANNED CHANGES' DETAILS
LISTED CHANGE DETAILS
</p>
<div class="tip">
<p data-font="sans-bold">
Admin only control mode
Add support for Crunchyroll
</p>
</div>
<div class="tip">
<p data-font="sans-bold">
Interactive system messages
Add support for Google Play Movies & TV
</p>
</div>
<div class="tip">
<p data-font="sans-bold">
Reply to messages with quoting
Completely rebuilt Chat UI
</p>
</div>
<div class="tip">
<p data-font="sans-bold">
Party name & pin history across sites
Gif UI revamped
</p>
</div>
<div class="tip">
<p data-font="sans-bold">
Shows sending messages
</p>
</div>
<div class="tip">
<p data-font="sans-bold">
Resizable sidebar
</p>
</div>
<div class="tip">
<p data-font="sans-bold">
New Welcome experience (chromewatchparty.com/welcome)
</p>
</div>
<div class="tip">
<p data-font="sans-bold">
Clear party and pin history
</p>
</div>
<div class="tip">
<p data-font="sans-bold">
Fix header bug on website
</p>
</div>
<div class="tip">
<p data-font="sans-bold">
Optimize website feature animations
</p>
</div>
<div class="tip">
<p data-font="sans-bold">
Admin only control mode (affects player sync and call)
</p>
</div>
<div class="tip">
<p data-font="sans-bold">
Anyone can start calls when not in admin only mode
</p>
</div>
<div class="tip">
<p data-font="sans-bold">
Cross site party name and pin history
</p>
</div>
<div class="tip">
<p data-font="sans-bold">
Volume control for users on call
</p>
</div>
<div class="tip">
<p data-font="sans-bold">
Interactive system messages in chat
</p>
</div>
<div class="tip">
<p data-font="sans-bold">
Alerts on sidebar create/join screen
</p>
</div>
<div class="tip">
<p data-font="sans-bold">
Autocomplete for party names and pins
</p>
</div>
<div class="tip">
<p data-font="sans-bold">
Proper page opens on update
</p>
</div>
<a
@@ -93,19 +295,30 @@ Peacock TV logo is a trademark and intellectual property of Peacock TV & NBC Uni
</div>
<footer data-font="sans">
<p class="title built-with">
<a href="../copyright/">COPYRIGHT</a> &copy; 2020 AYUSHYA AMITABH. ALL RIGHTS RESERVED.
<a href="../../copyright/">COPYRIGHT</a> &copy; 2020 AYUSHYA AMITABH.
ALL RIGHTS RESERVED.
</p>
<p class="title built-with">
BUILT WITH &#x1F49A; FOR <a href="https://www.google.com/chrome/">GOOGLE CHROME</a> USING
BUILT WITH &#x1F49A; FOR
<a href="https://www.google.com/chrome/">GOOGLE CHROME</a> USING
<a href="https://firebase.google.com/">FIREBASE</a>,
<a href="https://material-ui.com/">MATERIAL-UI</a>,
<a href="https://reactjs.org/">REACT JS</a>, <a href="https://nodejs.org/en/">NODE JS</a>,
<a href="https://giphy.com/">GIPHY</a>, & <a href="https://dolby.io/">DOLBY VOXEET</a>.
<a href="https://reactjs.org/">REACT JS</a>,
<a href="https://nodejs.org/en/">NODE JS</a>,
<a href="https://giphy.com/">GIPHY</a>, &
<a href="https://dolby.io/">DOLBY VOXEET</a>.
</p>
<a data-font="sans-bold" href="https://paypal.me/Ayushya" class="link-button">
<a
data-font="sans-bold"
href="https://paypal.me/Ayushya"
class="link-button"
>
Support Watch Party
</a>
<a data-font="sans-bold" href="https://request.chromewatchparty.com" class="link-button"
<a
data-font="sans-bold"
href="https://request.chromewatchparty.com"
class="link-button"
>Get Chrome Extension</a
>
<a
+18 -12
View File
@@ -4,18 +4,11 @@ Copyright (C) Ayushya Amitabh - All Rights Reserved
Unauthorized copying of this file, via any medium is strictly prohibited
Proprietary and confidential
Written by Ayushya Amitabh <admin@chromewatchparty.com>, 2020
https://www.chromewatchparty.com/copyright
=========================================================================
ALL CONTENT ON THIS WEBSITE, UNLESS OTHERWISE CITED ARE THE SOLE
INTELECTUAL PROPERTY OF AYUSHYA AMITABH, AND MAY NOT BE COPIED, EDITED,
OR REPRODUCED WITHOUT PERMISSION.
Netflix logo is a trademark and intellectual property of Netflix, Inc.
Disney+ logo is a trademark and intellectual property of The Walt Disney Company
Prime logo is a trademark and intellectual property of Amazon.com, Inc.
YouTube logo is a trademark and intellectual property of Google, Inc.
Hulu logo is a trademark and intellectual property of Hulu, LLC.
HBO Now and HBO Max logos are trademark and intellectual property of WarnerMedia Direct,LLC.
Peacock TV logo is a trademark and intellectual property of Peacock TV & NBC Universal.
ALL CONTENT ON WATCH PARTY'S WEBSITE OR IN WATCH PARTY'S CHROME EXTENSION APP, UNLESS OTHERWISE
CITED ARE THE SOLE INTELECTUAL PROPERTY OF AYUSHYA AMITABH, AND MAY NOT BE
COPIED, EDITED, OR REPRODUCED WITHOUT PERMISSION.
------------------------------------------------------------------------->
<html>
<head>
@@ -40,11 +33,22 @@ Peacock TV logo is a trademark and intellectual property of Peacock TV & NBC Uni
<script src="../scripts/Stickied.js"></script>
<title>Watch Party | Copyright</title>
</head>
<!-- Global site tag (gtag.js) - Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-87498806-2">
</script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'UA-87498806-2');
</script>
<body class="copyright">
<div class="landing">
<div class="logo-circle"></div>
<div class="logo-circle"></div>
<div class="logo-circle"></div>
<h1 data-font="serif" class="logo duplicate"><a href="../">WATCH PARTY</a> | COPYRIGHT</h1>
<h1 data-font="serif" class="logo"><a href="../">WATCH PARTY</a> | COPYRIGHT</h1>
<h5 data-font="sans" class="logo-tag">
Copyright &COPY; 2020 Ayushya Amitabh. All Rights Reserved.
@@ -68,7 +72,9 @@ Peacock TV logo is a trademark and intellectual property of Peacock TV & NBC Uni
Hulu logo is a trademark and intellectual property of Hulu, LLC.<br />
HBO Now and HBO Max logos are trademark and intellectual property of WarnerMedia Direct,
LLC.<br />
Peacock TV logo is a trademark and intellectual property of Peacock TV & NBC Universal.
Peacock TV logo is a trademark and intellectual property of Peacock TV & NBC Universal.<br />
Google Play Movies & TV logo is a trademark and intellectual property of Google, Inc.<br />
Crunchyroll logo is a trademark and intellectual property of Crunchyroll and Ellation, Inc.
<br />
<br />
</p>
+8
View File
@@ -3,6 +3,11 @@ Copyright (C) Ayushya Amitabh - All Rights Reserved
Unauthorized copying of this file, via any medium is strictly prohibited
Proprietary and confidential
Written by Ayushya Amitabh <admin@chromewatchparty.com>, 2020
https://www.chromewatchparty.com/copyright
=========================================================================
ALL CONTENT ON WATCH PARTY'S WEBSITE OR IN WATCH PARTY'S CHROME EXTENSION APP, UNLESS OTHERWISE
CITED ARE THE SOLE INTELECTUAL PROPERTY OF AYUSHYA AMITABH, AND MAY NOT BE
COPIED, EDITED, OR REPRODUCED WITHOUT PERMISSION.
************************************************************************/
@font-face {
@@ -35,10 +40,13 @@ Written by Ayushya Amitabh <admin@chromewatchparty.com>, 2020
}
[data-font='serif'] {
font-family: 'PixelSplitter', sans-serif;
font-display: swap;
}
[data-font='sans'] {
font-family: 'Glacial', sans-serif;
font-display: swap;
}
[data-font='sans-bold'] {
font-family: 'GlacialBold', sans-serif;
font-display: swap;
}
+324 -46
View File
@@ -4,126 +4,404 @@ Copyright (C) Ayushya Amitabh - All Rights Reserved
Unauthorized copying of this file, via any medium is strictly prohibited
Proprietary and confidential
Written by Ayushya Amitabh <admin@chromewatchparty.com>, 2020
https://www.chromewatchparty.com/copyright
=========================================================================
ALL CONTENT ON THIS WEBSITE, UNLESS OTHERWISE CITED ARE THE SOLE
INTELECTUAL PROPERTY OF AYUSHYA AMITABH, AND MAY NOT BE COPIED, EDITED,
OR REPRODUCED WITHOUT PERMISSION.
Netflix logo is a trademark and intellectual property of Netflix, Inc.
Disney+ logo is a trademark and intellectual property of The Walt Disney Company
Prime logo is a trademark and intellectual property of Amazon.com, Inc.
YouTube logo is a trademark and intellectual property of Google, Inc.
Hulu logo is a trademark and intellectual property of Hulu, LLC.
HBO Now and HBO Max logos are trademark and intellectual property of WarnerMedia Direct,LLC.
Peacock TV logo is a trademark and intellectual property of Peacock TV & NBC Universal.
ALL CONTENT ON WATCH PARTY'S WEBSITE OR IN WATCH PARTY'S CHROME EXTENSION APP, UNLESS OTHERWISE
CITED ARE THE SOLE INTELECTUAL PROPERTY OF AYUSHYA AMITABH, AND MAY NOT BE
COPIED, EDITED, OR REPRODUCED WITHOUT PERMISSION.
------------------------------------------------------------------------->
<html>
<head>
<link rel="apple-touch-icon" sizes="57x57" href="./icons/apple-icon-57x57.png" />
<link rel="apple-touch-icon" sizes="60x60" href="./icons/apple-icon-60x60.png" />
<link rel="apple-touch-icon" sizes="72x72" href="./icons/apple-icon-72x72.png" />
<link rel="apple-touch-icon" sizes="76x76" href="./icons/apple-icon-76x76.png" />
<link rel="apple-touch-icon" sizes="114x114" href="./icons/apple-icon-114x114.png" />
<link rel="apple-touch-icon" sizes="120x120" href="./icons/apple-icon-120x120.png" />
<link rel="apple-touch-icon" sizes="144x144" href="./icons/apple-icon-144x144.png" />
<link rel="apple-touch-icon" sizes="152x152" href="./icons/apple-icon-152x152.png" />
<link rel="apple-touch-icon" sizes="180x180" href="./icons/apple-icon-180x180.png" />
<link rel="icon" type="image/png" sizes="192x192" href="./icons/android-icon-192x192.png" />
<link rel="icon" type="image/png" sizes="32x32" href="./icons/favicon-32x32.png" />
<link rel="icon" type="image/png" sizes="96x96" href="./icons/favicon-96x96.png" />
<link rel="icon" type="image/png" sizes="16x16" href="./icons/favicon-16x16.png" />
<link
rel="apple-touch-icon"
sizes="57x57"
href="./icons/apple-icon-57x57.png"
/>
<link
rel="apple-touch-icon"
sizes="60x60"
href="./icons/apple-icon-60x60.png"
/>
<link
rel="apple-touch-icon"
sizes="72x72"
href="./icons/apple-icon-72x72.png"
/>
<link
rel="apple-touch-icon"
sizes="76x76"
href="./icons/apple-icon-76x76.png"
/>
<link
rel="apple-touch-icon"
sizes="114x114"
href="./icons/apple-icon-114x114.png"
/>
<link
rel="apple-touch-icon"
sizes="120x120"
href="./icons/apple-icon-120x120.png"
/>
<link
rel="apple-touch-icon"
sizes="144x144"
href="./icons/apple-icon-144x144.png"
/>
<link
rel="apple-touch-icon"
sizes="152x152"
href="./icons/apple-icon-152x152.png"
/>
<link
rel="apple-touch-icon"
sizes="180x180"
href="./icons/apple-icon-180x180.png"
/>
<link
rel="icon"
type="image/png"
sizes="192x192"
href="./icons/android-icon-192x192.png"
/>
<link
rel="icon"
type="image/png"
sizes="32x32"
href="./icons/favicon-32x32.png"
/>
<link
rel="icon"
type="image/png"
sizes="96x96"
href="./icons/favicon-96x96.png"
/>
<link
rel="icon"
type="image/png"
sizes="16x16"
href="./icons/favicon-16x16.png"
/>
<link rel="icon" href="./icons/favicon.ico" />
<link rel="manifest" href="./manifest.json" />
<meta name="msapplication-TileImage" content="./icons/ms-icon-144x144.png" />
<meta
name="msapplication-TileImage"
content="./icons/ms-icon-144x144.png"
/>
<link rel="stylesheet" type="text/css" href="./font.css" />
<link rel="stylesheet" type="text/css" href="./styles/index.css" />
<script src="./scripts/Stickied.js"></script>
<title>Watch Party</title>
</head>
<body>
<!-- Global site tag (gtag.js) - Google Analytics -->
<script
async
src="https://www.googletagmanager.com/gtag/js?id=UA-87498806-2"
></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag() {
dataLayer.push(arguments);
}
gtag("js", new Date());
gtag("config", "UA-87498806-2");
</script>
<body class="home">
<div class="landing">
<div class="logo-circle"></div>
<div class="logo-circle"></div>
<div class="logo-circle"></div>
<h1 data-font="serif" class="logo duplicate">WATCH PARTY</h1>
<h1 data-font="serif" class="logo">WATCH PARTY</h1>
<h5 data-font="sans" class="logo-tag">
WATCH YOUR FAVORITES,<br />WITH YOUR FAVORITE PEOPLE
</h5>
<p data-font="sans-bold" class="title subtitle">FREE. NO SIGNUP. NO BS.<br />WORKS WITH</p>
<p data-font="sans-bold" class="title subtitle">
FREE. NO SIGNUP. NO BS.<br />WORKS WITH
</p>
<div class="supported">
<div data-full-support class="supported-logo" id="netflix"></div>
<div data-full-support class="supported-logo" id="hulu"></div>
<div data-full-support class="supported-logo" id="disney"></div>
<div data-full-support class="supported-logo" id="prime"></div>
<div data-full-support class="supported-logo" id="youtube"></div>
<div data-beta-support class="supported-logo" id="hbonow"></div>
<div data-beta-support class="supported-logo" id="hbomax"></div>
<div data-beta-support class="supported-logo" id="peacock"></div>
<div data-full-support class="supported-logo" id="hbonow"></div>
<div data-full-support class="supported-logo" id="hbomax"></div>
<div data-full-support class="supported-logo" id="peacock"></div>
<div
data-upcoming-support="In v1.6.0"
class="supported-logo"
id="googleplay"
></div>
<div
data-upcoming-support="In v1.6.0"
class="supported-logo"
id="crunchyroll"
></div>
</div>
<a data-font="sans-bold" href="https://request.chromewatchparty.com" class="link-button"
>Get Chrome Extension</a
<a
data-font="sans-bold"
href="https://request.chromewatchparty.com"
class="link-button duplicate"
>
Get Chrome Extension
</a>
<a
data-font="sans-bold"
href="https://request.chromewatchparty.com"
class="link-button"
>
Get Chrome Extension
</a>
</div>
<div class="features">
<p data-font="sans-bold" class="title subtitle">FEATURES</p>
<div class="feature-card">
<div class="feature-icon" id="sync"></div>
<div class="feature-icon" id="sync">
<video loop muted autoplay src="./media/features/Sync.mp4"></video>
</div>
<p data-font="sans" class="feature-text">
Watch Party syncs play, pause and timestamp.
</p>
</div>
<div class="feature-card">
<div class="feature-icon" id="phone"></div>
<div class="feature-icon" id="phone">
<video loop muted autoplay src="./media/features/Phone.mp4"></video>
</div>
<p data-font="sans" class="feature-text">
Free group voice calls - for when text and GIFs just won't do.
</p>
</div>
<div class="feature-card">
<div class="feature-icon" id="chat"></div>
<div class="feature-icon" id="chat">
<video loop muted autoplay src="./media/features/Chat.mp4"></video>
</div>
<p data-font="sans" class="feature-text">
Unlimited gifs from Giphy in chat.
</p>
</div>
<div class="feature-card">
<div class="feature-icon" id="delete"></div>
<div class="feature-icon" id="delete">
<video loop muted autoplay src="./media/features/Delete.mp4"></video>
</div>
<p data-font="sans" class="feature-text">
What happens in Watch Party, stays in Watch Party - for 24 hours, then it's all gone.
What happens in Watch Party, stays in Watch Party - for 24 hours, then
it's all gone.
</p>
</div>
<div class="feature-card">
<div class="feature-icon" id="hideable"></div>
<div class="feature-icon" id="hideable">
<video
loop
muted
autoplay
src="./media/features/Hideable.mp4"
></video>
</div>
<p data-font="sans" class="feature-text">
There when you need it, hideable when you want to focus on the show.
</p>
</div>
<div class="feature-card">
<div class="feature-icon" id="custom"></div>
<div class="feature-icon" id="custom">
<video loop muted autoplay src="./media/features/Custom.mp4"></video>
</div>
<p data-font="sans" class="feature-text">
No party crashers. Custom party names secured with custom pins.
</p>
</div>
<div class="feature-card">
<div class="feature-icon" id="signup"></div>
<div class="feature-icon" id="signup">
<video loop muted autoplay src="./media/features/Signup.mp4"></video>
</div>
<p data-font="sans" class="feature-text">
No sign up, no login, no data taken from you.
</p>
</div>
</div>
<div id="getting-started" class="pop-features">
<p data-font="sans-bold" class="title subtitle">
GETTING STARTED
</p>
<div class="pop-feature">
<div id="title-card-container">
<div id="title-card">
<div data-font="serif" id="fab">WP</div>
<div id="sidebar"></div>
</div>
</div>
<div class="explanation">
<div>
<h4 data-font="sans-bold">TO BE OR NOT TO BE</h4>
<p data-font="sans">
Watch Party is built to not be in your way. There when you need
it, gone when you don't. The sidebar is now resizeable, and now
supports having access to Watch Party in fullscreen. (Use the
fullscreen button in the sidebar)
</p>
</div>
</div>
</div>
<div class="pop-feature alternate">
<div id="title-card-container">
<div id="title-card">
<h5 data-font="sans-bold" id="title-card-title">WATCH PARTY</h5>
<h3 data-font="sans" id="title-card-subtitle">YOUR PARTY NAME</h3>
<p data-font="sans" id="control-notification">
Allow chat notification when side panel is closed
<span>Notifications show up when the side bar is hidden</span>
</p>
<p data-font="sans" id="control-autorejoin">
Allow Watch Party to auto rejoin
<span
>Automatically rejoin party if you return to the page within 1
minute</span
>
</p>
<p data-font="sans" id="control-admin">
Enable admin only mode
<span>Only party admin can sync player and start calls</span>
</p>
</div>
</div>
<div class="explanation">
<div>
<h4 data-font="sans-bold">Stay in control</h4>
<p data-font="sans">
Find your settings by clicking on title card (when you're in a
party) or in the settings panel (when you're creating or joining)
</p>
</div>
</div>
</div>
<div class="pop-feature">
<div id="title-card-container">
<div id="title-card">
<h5 data-font="sans-bold" id="system-message">
Ayushya started the party.
</h5>
<div id="ti">
<div id="tn">SOMEONE</div>
<div id="tm">
<div id="tt">
<span>12:00:00 PM</span>
<div id="ri"></div>
</div>
<p>Hiya buddy!</p>
</div>
</div>
<div id="ti" class="mm">
<div id="tn">AYUSHYA</div>
<div id="tr">Hiya buddy!</div>
<div id="tm">
<div id="tt">
<span>12:00:00 PM</span>
<div id="ri"></div>
</div>
<p>OH GOD NO! PLEASE GOD NO! NO!</p>
</div>
</div>
<div id="ti">
<div id="tn">SOMEONE</div>
<div id="tm">
<div id="tt">
<span>12:00:00 PM</span>
<div id="ri"></div>
</div>
<img
src="https://media.giphy.com/media/d10dMmzqCYqQ0/giphy.gif"
/>
</div>
</div>
<div id="ti" class="mm">
<div id="tn">SOMEONE</div>
<div id="tr">
<img
src="https://media.giphy.com/media/d10dMmzqCYqQ0/giphy.gif"
/>
</div>
<div id="tm">
<div id="tt">
<span>12:00:00 PM</span>
<div id="ri"></div>
</div>
<p>Let's watch The Office</p>
</div>
</div>
</div>
</div>
<div class="explanation">
<div>
<h4 data-font="sans-bold">FULL FEATURED CHAT</h4>
<p data-font="sans">
A brand new, completely revamped chat user interface, now has
replies, shows pending messages, and improves the Giphy GIF picker
</p>
</div>
</div>
</div>
<div class="pop-feature alternate">
<div id="title-card-container">
<div id="title-card">
<h5 data-font="sans-bold" id="title-card-title">WATCH PARTY</h5>
<h3 data-font="sans" id="title-card-subtitle">ANIME WATCH PARTY</h3>
<h6 data-font="sans" id="title-card-tagline">USERS IN PARTY</h6>
<p data-font="sans" id="call-user-chip">Ayushya</p>
<p data-font="sans" id="call-user-chip">Someone</p>
<p data-font="sans" id="call-user-chip">Third Party</p>
<p data-font="sans" id="call-user-chip">Won't Stop Talking</p>
<h6 data-font="sans" id="title-card-tagline">USERS ON CALL</h6>
<p data-font="sans" id="call-user-chip">Ayushya</p>
<p data-font="sans" id="call-user-chip" class="not-me-user-chip">
Someone
</p>
<p data-font="sans" id="call-user-chip" class="not-me-user-chip">
Won't Stop Talking
<input type="range" value="2" min="0" max="10" />
</p>
</div>
</div>
<div class="explanation">
<div>
<h4 data-font="sans-bold">MADE FOR YOU & YOUR GANG</h4>
<p data-font="sans">
See who's in your party, get on a call with your party, and volume
control for the person who just won't stop talking.
</p>
</div>
</div>
</div>
</div>
<footer data-font="sans">
<p class="title built-with">
<a href="./copyright/">COPYRIGHT</a> &copy; 2020 AYUSHYA AMITABH. ALL RIGHTS RESERVED.
<a href="./copyright/">COPYRIGHT</a> &copy; 2020 AYUSHYA AMITABH. ALL
RIGHTS RESERVED.
</p>
<p class="title built-with">
BUILT WITH &#x1F49A; FOR <a href="https://www.google.com/chrome/">GOOGLE CHROME</a> USING
BUILT WITH &#x1F49A; FOR
<a href="https://www.google.com/chrome/">GOOGLE CHROME</a> USING
<a href="https://firebase.google.com/">FIREBASE</a>,
<a href="https://material-ui.com/">MATERIAL-UI</a>,
<a href="https://reactjs.org/">REACT JS</a>, <a href="https://nodejs.org/en/">NODE JS</a>,
<a href="https://giphy.com/">GIPHY</a>, & <a href="https://dolby.io/">DOLBY VOXEET</a>.
<a href="https://reactjs.org/">REACT JS</a>,
<a href="https://nodejs.org/en/">NODE JS</a>,
<a href="https://giphy.com/">GIPHY</a>, &
<a href="https://dolby.io/">DOLBY VOXEET</a>.
</p>
<a data-font="sans-bold" href="https://paypal.me/Ayushya" class="link-button">
<a
data-font="sans-bold"
href="https://paypal.me/Ayushya"
class="link-button"
>
Support Watch Party
</a>
<a data-font="sans-bold" href="https://request.chromewatchparty.com" class="link-button"
<a
data-font="sans-bold"
href="https://request.chromewatchparty.com"
class="link-button"
>Get Chrome Extension</a
>
<a
-6
View File
@@ -1,9 +1,3 @@
/************************************************************************
Copyright (C) Ayushya Amitabh - All Rights Reserved
Unauthorized copying of this file, via any medium is strictly prohibited
Proprietary and confidential
Written by Ayushya Amitabh <admin@chromewatchparty.com>, 2020
************************************************************************/
{
"name": "Watch Party",
"short_name": "WP",
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

+17 -4
View File
@@ -3,30 +3,43 @@ Copyright (C) Ayushya Amitabh - All Rights Reserved
Unauthorized copying of this file, via any medium is strictly prohibited
Proprietary and confidential
Written by Ayushya Amitabh <admin@chromewatchparty.com>, 2020
https://www.chromewatchparty.com/copyright
=========================================================================
ALL CONTENT ON WATCH PARTY'S WEBSITE OR IN WATCH PARTY'S CHROME EXTENSION APP, UNLESS OTHERWISE
CITED ARE THE SOLE INTELECTUAL PROPERTY OF AYUSHYA AMITABH, AND MAY NOT BE
COPIED, EDITED, OR REPRODUCED WITHOUT PERMISSION.
************************************************************************/
var LOGO_INIT = 0;
var GET_INIT = 0;
function handleSticky() {
const logo = document.querySelector('.logo');
const get = document.querySelector('.landing .link-button');
if (window.scrollY >= LOGO_INIT + logo.offsetHeight * 2) {
const logo = document.querySelector('.logo.duplicate');
const get = document.querySelector('.landing .link-button.duplicate');
if (window.scrollY >= LOGO_INIT + logo.offsetHeight) {
logo.classList.add('stickied');
} else {
logo.classList.remove('stickied');
}
if (get) {
if (window.scrollY >= GET_INIT + get.offsetHeight * 2) {
if (window.scrollY >= GET_INIT + get.offsetHeight) {
get.classList.add('stickied');
} else {
get.classList.remove('stickied');
}
}
}
function handleLogoMargin () {
const logo = document.querySelector('.logo.duplicate');
if (logo) document.documentElement.style.setProperty('--logo-offset', '-' + logo.offsetHeight + 'px');
const linkButton = document.querySelector('.link-button.duplicate');
if (linkButton) document.documentElement.style.setProperty('--link-button-offset', '-' + linkButton.offsetHeight + 'px');
}
window.onload = function () {
LOGO_INIT = document.querySelector('.logo').offsetTop;
GET_INIT = document.querySelector('.landing .link-button')
? document.querySelector('.landing .link-button').offsetTop
: 0;
this.handleLogoMargin();
window.addEventListener('scroll', this.handleSticky);
window.addEventListener('resize', this.handleLogoMargin);
};
+5
View File
@@ -3,6 +3,11 @@ Copyright (C) Ayushya Amitabh - All Rights Reserved
Unauthorized copying of this file, via any medium is strictly prohibited
Proprietary and confidential
Written by Ayushya Amitabh <admin@chromewatchparty.com>, 2020
https://www.chromewatchparty.com/copyright
=========================================================================
ALL CONTENT ON WATCH PARTY'S WEBSITE OR IN WATCH PARTY'S CHROME EXTENSION APP, UNLESS OTHERWISE
CITED ARE THE SOLE INTELECTUAL PROPERTY OF AYUSHYA AMITABH, AND MAY NOT BE
COPIED, EDITED, OR REPRODUCED WITHOUT PERMISSION.
************************************************************************/
html {
+421 -13
View File
@@ -3,6 +3,11 @@ Copyright (C) Ayushya Amitabh - All Rights Reserved
Unauthorized copying of this file, via any medium is strictly prohibited
Proprietary and confidential
Written by Ayushya Amitabh <admin@chromewatchparty.com>, 2020
https://www.chromewatchparty.com/copyright
=========================================================================
ALL CONTENT ON WATCH PARTY'S WEBSITE OR IN WATCH PARTY'S CHROME EXTENSION APP, UNLESS OTHERWISE
CITED ARE THE SOLE INTELECTUAL PROPERTY OF AYUSHYA AMITABH, AND MAY NOT BE
COPIED, EDITED, OR REPRODUCED WITHOUT PERMISSION.
************************************************************************/
@keyframes circleFloat {
@@ -71,6 +76,17 @@ body {
letter-spacing: 2.2rem;
}
.logo.duplicate {
margin-bottom: var(--logo-offset);
opacity: 0;
height: 0;
}
.link-button.duplicate {
margin-bottom: var(--link-button-offset);
opacity: 0;
}
.logo-tag {
color: var(--primary-text);
font-size: 3.2rem;
@@ -129,16 +145,17 @@ body {
margin: 10rem 0;
}
.feature-icon {
padding: 5rem;
background-position: left !important;
background-size: contain !important;
background-repeat: no-repeat !important;
background-color: black;
margin: 2.5rem 0;
display: table;
width: fit-content;
.feature-icon video {
border-radius: 100%;
width: 10rem;
height: auto;
margin: 0 auto;
margin-top: 9rem;
transition: all ease-in-out 0.3s;
}
.feature-card .feature-icon video {
transform: scale(2.5) translateY(-50%) translateX(1.5rem);
}
.feature-text {
@@ -207,7 +224,7 @@ footer .link-button {
.welcome .landing#tips .tip {
padding: 3rem 0;
border-bottom: 0.5px solid var(--primary-medium);
border-bottom: 0.5px solid var(--cta);
padding-right: 3rem;
}
@@ -239,7 +256,7 @@ footer .link-button {
.landing .supported .supported-logo[data-full-support]::before,
.landing .supported .supported-logo[data-beta-support]::before,
.landing .supported .supported-logo[data-upcoming-support]::before {
content: 'FULLY SUPPORTED';
content: "FULLY SUPPORTED";
position: absolute;
width: 100%;
bottom: 0;
@@ -250,16 +267,407 @@ footer .link-button {
font-size: 0.8rem;
text-align: center;
color: var(--primary);
font-family: 'GlacialBold';
font-family: "GlacialBold";
background-color: var(--cta);
padding: 1rem 0;
}
.landing .supported .supported-logo[data-beta-support]::before {
content: 'BETA SUPPORT';
content: "BETA SUPPORT";
}
.landing .supported .supported-logo[data-upcoming-support]::before {
content: attr(data-upcoming-support);
text-transform: uppercase;
}
.home .pop-features {
margin-bottom: 50px;
}
.pop-features {
padding: 35px;
padding-left: 70px;
}
.pop-features .title {
margin-bottom: 70px;
}
.pop-feature {
width: 100%;
height: fit-content;
display: flex;
flex-direction: column;
}
.pop-feature > * {
flex: 1;
margin: 40px 0;
}
#title-card {
border-radius: 26px;
background: var(--primary-light);
width: 500px;
position: relative;
height: 500px;
box-shadow: 0px 0px 84px var(--primary-medium);
margin: 0 auto;
}
div#title-card {
width: calc(500px - 60px);
height: calc(500px - 60px);
padding: 30px;
}
h5#title-card-title {
font-size: 20px;
color: var(--cta);
letter-spacing: 5px;
}
h3#title-card-subtitle {
font-size: 30px;
letter-spacing: 5px;
margin: 7.5px 0px;
color: var(--primary-text);
}
p#control-notification,
p#control-autorejoin,
p#control-admin {
font-size: 15px;
padding: 10px 55px;
background: var(--primary);
border-radius: 26px;
color: var(--primary-text);
box-shadow: 0px 0px 20px var(--primary-medium);
margin: 35px 0px;
width: max-content;
}
p#control-notification {
margin-left: -150px;
}
p#control-autorejoin {
margin-left: 100px;
}
p#control-admin {
margin-left: -75px;
}
p#control-notification span,
p#control-autorejoin span,
p#control-admin span {
display: block;
color: #949494;
}
.pop-feature .explanation > div {
margin: auto auto;
align-items: center;
max-width: 55%;
}
.pop-feature .explanation div h4 {
color: var(--primary-text);
font-size: 20px;
text-transform: uppercase;
letter-spacing: 5px;
margin-bottom: 50px;
}
.pop-feature .explanation p {
color: var(--primary-text);
font-size: 16px;
text-transform: uppercase;
letter-spacing: 5px;
line-height: 2;
}
.pop-feature .explanation {
display: flex;
justify-content: center;
align-items: center;
text-align: center;
}
h5#system-message {
text-align: center;
color: var(--primary-text);
font-size: 16px;
letter-spacing: 2px;
margin: 5px 0;
}
p#call-user-chip {
display: inline-block;
font-size: 20px;
padding: 10px 25px;
color: var(--primary-text);
border-radius: 26px;
background: var(--cta);
color: var(--primary-medium);
margin-right: 20px;
position: relative;
margin-bottom: 20px;
}
p#call-user-chip.not-me-user-chip {
background: var(--primary);
color: var(--primary-text);
display: inline-flex;
margin: 0;
max-width: fit-content;
align-items: center;
justify-content: center;
}
p#call-user-chip.not-me-user-chip input {
margin-left: 20px;
}
h6#title-card-tagline {
color: var(--primary-medium);
font-size: 15px;
color: var(--primary);
margin: 20px 0;
letter-spacing: 3.5px;
}
@keyframes circleMove {
0% {
right: -40px;
bottom: -40px;
}
35% {
right: 200px;
bottom: 350px;
}
60% {
right: 200px;
bottom: 350px;
}
75% {
right: -40px;
bottom: -40px;
}
100% {
right: -40px;
bottom: -40px;
}
}
@keyframes rectSize {
0% {
width: 0;
}
35% {
width: 300px;
}
60% {
width: 300px;
}
75% {
width: 0;
}
100% {
width: 0;
}
}
div#fab {
background: var(--primary);
width: 200px;
height: 200px;
border-radius: 100px;
display: flex;
justify-content: center;
align-items: center;
font-size: 60px;
letter-spacing: 5px;
text-align: center;
color: var(--cta);
position: absolute;
right: 250px;
bottom: 250px;
animation: circleMove 2s linear 0s infinite alternate !important;
z-index: 99;
filter: drop-shadow(0px 0px 20px var(--primary));
}
div#title-card {
position: relative;
}
div#sidebar {
width: 300px;
height: 120%;
right: 0;
position: absolute;
background: var(--cta);
top: -10%;
border-top-right-radius: 26px;
border-bottom-right-radius: 26px;
animation: rectSize 2s linear 0s infinite alternate !important;
box-shadow: -5px 0px 50px var(--primary-medium);
}
/*
T H R E A D
D E M O S
*/
#ti * {
font-size: 14px !important;
font-family: "Glacial";
letter-spacing: 1.1px;
}
#ti {
color: white;
width: fit-content;
margin: 10px 0;
width: 100%;
}
#tm {
width: 100%;
position: relative;
}
#tm #tt {
height: 0;
overflow-y: visible;
font-size: 10px;
transition: all ease-in-out 0.3s;
/* transition-delay: 0.1s; */
white-space: nowrap;
display: flex;
align-items: center;
opacity: 0;
color: #9f9f9f;
width: 100%;
display: flex;
justify-content: center;
align-items: center;
flex-direction: row-reverse;
}
#tm #tt span {
flex: 1;
text-align: center;
}
#tm #tt #ri {
position: absolute;
display: flex;
justify-content: center;
align-items: center;
padding: 15px;
color: #c0f300;
font-size: 20px;
border-radius: 26px;
z-index: 99 !important;
left: 0;
cursor: pointer;
background-color: white;
background-image: url("https://www.materialui.co/materialIcons/content/reply_grey_192x192.png");
background-size: 75%;
background-repeat: no-repeat;
background-position: center;
bottom: 0;
transition: all ease-in-out 0.3s;
/* transition-delay: 0.1s; */
}
#ti:hover #tm #tt {
height: 24px;
margin-bottom: 7px;
opacity: 1;
}
#tm p {
max-width: 40%;
padding: 5px 15px;
background: #424242;
border-radius: 26px;
z-index: 99 !important;
transition: all ease-in-out 0.3s;
/* transition-delay: 0.1s; */
width: fit-content;
}
#tm img {
max-width: 40%;
border-radius: 26px;
transition: all ease-in-out 0.3s;
/* transition-delay: 0.1s; */
z-index: 99;
}
#ti:hover #tm p,
#ti:hover #tm img {
margin-left: 40px;
}
#tm #tt #ri:hover {
opacity: 0.6;
}
#tn {
font-size: 11px;
margin-top: 15px;
margin-bottom: 7px;
color: var(--primary-text);
}
#tr {
font-size: 11px;
margin-bottom: -20px;
padding: 7.5px 15px;
padding-bottom: 25px;
border-radius: 13px;
background: #222222;
width: fit-content;
max-width: 35%;
z-index: 99 !important;
transition: all ease-in-out 0.3s;
/* transition-delay: 0.1s; */
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
cursor: pointer;
}
#tr img {
max-height: 50px;
border-radius: 26px;
transition: all ease-in-out 0.3s;
/* transition-delay: 0.1s; */
}
#ti:hover #tr {
margin-bottom: 0;
padding-bottom: 7.5px;
}
#tr:hover {
background: #111111;
}
#ti.mm {
display: flex;
flex-direction: column;
align-items: flex-end;
}
#ti.mm #tn {
display: none;
}
#ti.mm #tm {
display: flex;
flex-direction: column;
align-items: flex-end;
}
#ti.mm #tm p {
text-align: right;
}
#ti.mm #tm #tt {
flex-direction: row;
}
#ti.mm #tm #tt #ri {
right: 0;
left: unset;
}
#ti.mm:hover #tm p,
#ti.mm:hover #tm img {
margin-left: 0;
margin-right: 40px;
}
+5
View File
@@ -3,6 +3,11 @@ Copyright (C) Ayushya Amitabh - All Rights Reserved
Unauthorized copying of this file, via any medium is strictly prohibited
Proprietary and confidential
Written by Ayushya Amitabh <admin@chromewatchparty.com>, 2020
https://www.chromewatchparty.com/copyright
=========================================================================
ALL CONTENT ON WATCH PARTY'S WEBSITE OR IN WATCH PARTY'S CHROME EXTENSION APP, UNLESS OTHERWISE
CITED ARE THE SOLE INTELECTUAL PROPERTY OF AYUSHYA AMITABH, AND MAY NOT BE
COPIED, EDITED, OR REPRODUCED WITHOUT PERMISSION.
************************************************************************/
@import './variables.css';
+43
View File
@@ -3,6 +3,11 @@ Copyright (C) Ayushya Amitabh - All Rights Reserved
Unauthorized copying of this file, via any medium is strictly prohibited
Proprietary and confidential
Written by Ayushya Amitabh <admin@chromewatchparty.com>, 2020
https://www.chromewatchparty.com/copyright
=========================================================================
ALL CONTENT ON WATCH PARTY'S WEBSITE OR IN WATCH PARTY'S CHROME EXTENSION APP, UNLESS OTHERWISE
CITED ARE THE SOLE INTELECTUAL PROPERTY OF AYUSHYA AMITABH, AND MAY NOT BE
COPIED, EDITED, OR REPRODUCED WITHOUT PERMISSION.
************************************************************************/
.features,
@@ -13,3 +18,41 @@ Written by Ayushya Amitabh <admin@chromewatchparty.com>, 2020
.landing video {
max-width: 45%;
}
.home .pop-features {
margin-bottom: 100px;
}
.pop-feature {
flex-direction: row;
}
.pop-feature {
margin: 100px 0;
}
.pop-feature > * {
margin: 0;
}
.pop-feature .explanation {
text-align: left;
}
.pop-feature .explanation div {
margin-left: 0;
padding: 50px;
}
.pop-feature.alternate {
flex-direction: row-reverse;
}
.pop-feature.alternate .explanation {
text-align: right;
}
.pop-feature.pop-feature.alternate .explanation div {
margin-left: auto;
margin-right: 0;
}
+29 -28
View File
@@ -3,6 +3,11 @@ Copyright (C) Ayushya Amitabh - All Rights Reserved
Unauthorized copying of this file, via any medium is strictly prohibited
Proprietary and confidential
Written by Ayushya Amitabh <admin@chromewatchparty.com>, 2020
https://www.chromewatchparty.com/copyright
=========================================================================
ALL CONTENT ON WATCH PARTY'S WEBSITE OR IN WATCH PARTY'S CHROME EXTENSION APP, UNLESS OTHERWISE
CITED ARE THE SOLE INTELECTUAL PROPERTY OF AYUSHYA AMITABH, AND MAY NOT BE
COPIED, EDITED, OR REPRODUCED WITHOUT PERMISSION.
************************************************************************/
html {
@@ -31,7 +36,7 @@ body {
position: relative;
}
.link-button::before {
content: '';
content: "";
height: 100%;
top: 0;
width: 0;
@@ -61,13 +66,16 @@ body {
width: 100%;
left: 0;
top: 0;
opacity: 1;
padding: 3rem 10rem;
height: auto;
z-index: 999 !important;
}
.landing .link-button.stickied {
position: fixed;
right: 0rem;
top: 0;
opacity: 1;
border-radius: 0;
font-size: 2rem;
z-index: 1999 !important;
@@ -111,21 +119,7 @@ body {
transition: all 0.3s ease-in;
position: relative;
}
.feature-icon {
z-index: 1;
background-position: center !important;
transition: all ease-out 0.3s;
border-radius: 100%;
width: 0;
margin: 0 auto;
height: 0;
background-size: cover !important;
padding: 5rem;
background-color: black;
position: absolute;
top: 13rem;
left: 13rem;
}
.feature-card .feature-text {
z-index: 1;
display: table;
@@ -145,13 +139,19 @@ body {
color: var(--primary-light);
line-height: initial;
opacity: 1;
margin-top: -1.75rem;
}
.feature-card:hover .feature-icon {
position: relative;
top: -35%;
left: unset;
transform: scale(3);
margin-bottom: 3rem;
.feature-icon {
display: flex;
}
.feature-card .feature-icon video {
transform: none;
}
.feature-card:hover .feature-icon video {
transform: scale(3) translateY(-50%);
}
footer {
@@ -187,9 +187,10 @@ footer .link-button {
.welcome .logo a::before,
.copyright .logo a::before {
content: '';
content: "";
height: 100%;
top: 0;
top: -3px;
padding-top: 2px;
width: 0;
position: absolute;
left: -1rem;
@@ -218,7 +219,7 @@ footer .link-button {
}
.welcome .landing#tips .tip {
padding-right: 0;
padding-right: 100px;
transition: all ease-out 0.3s;
}
@@ -231,7 +232,7 @@ footer .link-button {
}
.welcome .landing .supported-logo::before {
content: 'START WATCHING';
content: "START WATCHING";
position: absolute;
width: 100%;
bottom: 0;
@@ -242,7 +243,7 @@ footer .link-button {
font-size: 1.5rem;
text-align: center;
color: var(--primary);
font-family: 'GlacialBold';
font-family: "GlacialBold";
background-color: var(--cta);
padding: 1rem 0;
}
@@ -265,7 +266,7 @@ footer .link-button {
}
.welcome .landing#tips .tip h6 {
color: var(--primary-light);
color: var(--primary-medium);
transition: all ease-out 0.3s;
}
+5
View File
@@ -3,6 +3,11 @@ Copyright (C) Ayushya Amitabh - All Rights Reserved
Unauthorized copying of this file, via any medium is strictly prohibited
Proprietary and confidential
Written by Ayushya Amitabh <admin@chromewatchparty.com>, 2020
https://www.chromewatchparty.com/copyright
=========================================================================
ALL CONTENT ON WATCH PARTY'S WEBSITE OR IN WATCH PARTY'S CHROME EXTENSION APP, UNLESS OTHERWISE
CITED ARE THE SOLE INTELECTUAL PROPERTY OF AYUSHYA AMITABH, AND MAY NOT BE
COPIED, EDITED, OR REPRODUCED WITHOUT PERMISSION.
************************************************************************/
.welcome .landing video {
+7 -2
View File
@@ -3,12 +3,17 @@ Copyright (C) Ayushya Amitabh - All Rights Reserved
Unauthorized copying of this file, via any medium is strictly prohibited
Proprietary and confidential
Written by Ayushya Amitabh <admin@chromewatchparty.com>, 2020
https://www.chromewatchparty.com/copyright
=========================================================================
ALL CONTENT ON WATCH PARTY'S WEBSITE OR IN WATCH PARTY'S CHROME EXTENSION APP, UNLESS OTHERWISE
CITED ARE THE SOLE INTELECTUAL PROPERTY OF AYUSHYA AMITABH, AND MAY NOT BE
COPIED, EDITED, OR REPRODUCED WITHOUT PERMISSION.
************************************************************************/
:root {
--primary: black;
--primary-medium: #232323;
--primary-light: #454545;
--primary-medium: #464646;
--primary-light: #626262;
--cta: #c0f300;
--primary-text: white;
}
+17 -28
View File
@@ -3,6 +3,11 @@ Copyright (C) Ayushya Amitabh - All Rights Reserved
Unauthorized copying of this file, via any medium is strictly prohibited
Proprietary and confidential
Written by Ayushya Amitabh <admin@chromewatchparty.com>, 2020
https://www.chromewatchparty.com/copyright
=========================================================================
ALL CONTENT ON WATCH PARTY'S WEBSITE OR IN WATCH PARTY'S CHROME EXTENSION APP, UNLESS OTHERWISE
CITED ARE THE SOLE INTELECTUAL PROPERTY OF AYUSHYA AMITABH, AND MAY NOT BE
COPIED, EDITED, OR REPRODUCED WITHOUT PERMISSION.
************************************************************************/
:root {
@@ -14,48 +19,32 @@ Written by Ayushya Amitabh <admin@chromewatchparty.com>, 2020
font-size: 10px;
}
.supported-logo#netflix {
background-image: url('../media/logos/Netflix.png');
background-image: url("../media/logos/Netflix.png");
}
.supported-logo#hulu {
background-image: url('../media/logos/Hulu.png');
background-image: url("../media/logos/Hulu.png");
}
.supported-logo#disney {
background-image: url('../media/logos/DisneyColor.png');
background-image: url("../media/logos/DisneyColor.png");
}
.supported-logo#prime {
background-image: url('../media/logos/PrimeColor.png');
background-image: url("../media/logos/PrimeColor.png");
}
.supported-logo#youtube {
background-image: url('../media/logos/YoutubeColor.png');
background-image: url("../media/logos/YoutubeColor.png");
}
.supported-logo#hbonow {
background-image: url('../media/logos/HBONowBlack.png');
background-image: url("../media/logos/HBONowBlack.png");
}
.supported-logo#hbomax {
background-image: url('../media/logos/HBOMax.png');
background-image: url("../media/logos/HBOMax.png");
}
.supported-logo#peacock {
background-image: url('../media/logos/PeacockColor.png');
background-image: url("../media/logos/PeacockColor.png");
}
.feature-icon#phone {
background-image: url('../media/features/Phone.gif');
.supported-logo#googleplay {
background-image: url("../media/logos/GooglePlay.png");
}
.feature-icon#delete {
background-image: url('../media/features/Delete.gif');
}
.feature-icon#signup {
background-image: url('../media/features/Signup.gif');
}
.feature-icon#sync {
background-image: url('../media/features/Sync.gif');
}
.feature-icon#custom {
background-image: url('../media/features/Custom.gif');
}
.feature-icon#chat {
background-image: url('../media/features/Chat.gif');
}
.feature-icon#hideable {
background-image: url('../media/features/Hideable.gif');
.supported-logo#crunchyroll {
background-image: url("../media/logos/Crunchyroll.png");
}
+165 -24
View File
@@ -4,18 +4,11 @@ Copyright (C) Ayushya Amitabh - All Rights Reserved
Unauthorized copying of this file, via any medium is strictly prohibited
Proprietary and confidential
Written by Ayushya Amitabh <admin@chromewatchparty.com>, 2020
https://www.chromewatchparty.com/copyright
=========================================================================
ALL CONTENT ON THIS WEBSITE, UNLESS OTHERWISE CITED ARE THE SOLE
INTELECTUAL PROPERTY OF AYUSHYA AMITABH, AND MAY NOT BE COPIED, EDITED,
OR REPRODUCED WITHOUT PERMISSION.
Netflix logo is a trademark and intellectual property of Netflix, Inc.
Disney+ logo is a trademark and intellectual property of The Walt Disney Company
Prime logo is a trademark and intellectual property of Amazon.com, Inc.
YouTube logo is a trademark and intellectual property of Google, Inc.
Hulu logo is a trademark and intellectual property of Hulu, LLC.
HBO Now and HBO Max logos are trademark and intellectual property of WarnerMedia Direct,LLC.
Peacock TV logo is a trademark and intellectual property of Peacock TV & NBC Universal.
ALL CONTENT ON WATCH PARTY'S WEBSITE OR IN WATCH PARTY'S CHROME EXTENSION APP, UNLESS OTHERWISE
CITED ARE THE SOLE INTELECTUAL PROPERTY OF AYUSHYA AMITABH, AND MAY NOT BE
COPIED, EDITED, OR REPRODUCED WITHOUT PERMISSION.
------------------------------------------------------------------------->
<html>
<head>
@@ -40,18 +33,26 @@ Peacock TV logo is a trademark and intellectual property of Peacock TV & NBC Uni
<script src="../scripts/Stickied.js"></script>
<title>Watch Party | Welcome</title>
</head>
<!-- Global site tag (gtag.js) - Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-87498806-2">
</script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'UA-87498806-2');
</script>
<body class="welcome">
<div class="landing">
<div class="logo-circle"></div>
<div class="logo-circle"></div>
<div class="logo-circle"></div>
<h1 data-font="serif" class="logo duplicate"><a href="../">WATCH PARTY</a></h1>
<h1 data-font="serif" class="logo"><a href="../">WATCH PARTY</a></h1>
<h5 data-font="sans" class="logo-tag">
WELCOME TO WATCH PARTY
</h5>
<p data-font="sans-bold" class="title subtitle">Watch Party lives in the bottom right corner of your screen. <br /> It's there when you need it and hidden when you don't.</p>
<video src="../media/features/Tutorial.mp4" autoplay muted loop> </video>
<a data-font="sans-bold" href="../changes/" class="link-button">See What's New</a>
<p data-font="sans-bold" class="title subtitle">
WATCH TOGETHER ON
</p>
@@ -63,14 +64,162 @@ Peacock TV logo is a trademark and intellectual property of Peacock TV & NBC Uni
<a href="https://www.youtube.com"><div class="supported-logo" id="youtube"></div></a>
<a href="https://play.hbonow.com"><div class="supported-logo" id="hbonow"></div></a>
<a href="https://play.hbomax.com"><div class="supported-logo" id="hbomax"></div></a>
<a href="https://www.peacocktv.com"><div class="supported-logo" id="peacock"></div></div></a>
<a href="https://www.peacocktv.com"><div class="supported-logo" id="peacock"></div></a>
<a href="https://play.google.com/movies"><div class="supported-logo" id="googleplay"></div></a>
<a href="https://www.crunchyroll.com"><div class="supported-logo" id="crunchyroll"></div></a>
</div>
</div>
</div>
<div id="getting-started" class="pop-features">
<p data-font="sans-bold" class="title">
GETTING STARTED
</p>
<div class="pop-feature">
<div id="title-card-container">
<div id="title-card">
<div data-font="serif" id="fab">WP</div>
<div id="sidebar"></div>
</div>
</div>
<div class="explanation">
<div>
<h4 data-font="sans-bold">TO BE OR NOT TO BE</h4>
<p data-font="sans">Watch Party is built to not be in your way. There when you need it, gone when you don't. The sidebar is now resizeable, and now supports having access to Watch Party in fullscreen. (Use the fullscreen button in the sidebar)</p>
</div>
</div>
</div>
<div class="pop-feature alternate">
<div id="title-card-container">
<div id="title-card">
<h5 data-font="sans-bold" id="title-card-title">WATCH PARTY</h5>
<h3 data-font="sans" id="title-card-subtitle">YOUR PARTY NAME</h3>
<p data-font="sans" id="control-notification">
Allow chat notification when side panel is closed
<span>Notifications show up when the side bar is hidden</span>
</p>
<p data-font="sans" id="control-autorejoin">
Allow Watch Party to auto rejoin
<span>Automatically rejoin party if you return to the page within 1 minute</span>
</p>
<p data-font="sans" id="control-admin">
Enable admin only mode
<span>Only party admin can sync player and start calls</span>
</p>
</div>
</div>
<div class="explanation">
<div>
<h4 data-font="sans-bold">Stay in control</h4>
<p data-font="sans">Find your settings by clicking on title card (when you're in a party) or in the settings panel (when you're creating or joining) </p>
</div>
</div>
</div>
<div class="pop-feature">
<div id="title-card-container">
<div id="title-card">
<h5 data-font="sans-bold" id="system-message">Ayushya started the party.</h5>
<div id="ti">
<div id="tn">SOMEONE</div>
<div id="tm">
<div id="tt">
<span>12:00:00 PM</span>
<div id="ri"></div></div>
<p>Hiya buddy!</p>
</div>
</div>
<div id="ti" class="mm">
<div id="tn">AYUSHYA</div>
<div id="tr">Hiya buddy!
</div>
<div id="tm">
<div id="tt">
<span>12:00:00 PM</span>
<div id="ri"></div></div>
<p>OH GOD NO! PLEASE GOD NO! NO!</p>
</div>
</div>
<div id="ti">
<div id="tn">SOMEONE</div>
<div id="tm">
<div id="tt">
<span>12:00:00 PM</span>
<div id="ri"></div>
</div>
<img src="https://media.giphy.com/media/d10dMmzqCYqQ0/giphy.gif" />
</div>
</div>
<div id="ti" class="mm">
<div id="tn">SOMEONE</div>
<div id="tr">
<img src="https://media.giphy.com/media/d10dMmzqCYqQ0/giphy.gif" /></div>
<div id="tm">
<div id="tt">
<span>12:00:00 PM</span>
<div id="ri"></div>
</div>
<p>Let's watch The Office</p>
</div>
</div>
</div>
</div>
<div class="explanation">
<div>
<h4 data-font="sans-bold">FULL FEATURED CHAT</h4>
<p data-font="sans">A brand new, completely revamped chat user interface, now has replies, shows pending messages, and improves the Giphy GIF picker</p>
</div>
</div>
</div>
<div class="pop-feature alternate">
<div id="title-card-container">
<div id="title-card">
<h5 data-font="sans-bold" id="title-card-title">WATCH PARTY</h5>
<h3 data-font="sans" id="title-card-subtitle">ANIME WATCH PARTY</h3>
<h6 data-font="sans" id="title-card-tagline">USERS IN PARTY</h6>
<p data-font="sans" id="call-user-chip">Ayushya</p>
<p data-font="sans" id="call-user-chip">Someone</p>
<p data-font="sans" id="call-user-chip">Third Party</p>
<p data-font="sans" id="call-user-chip">Won't Stop Talking</p>
<h6 data-font="sans" id="title-card-tagline">USERS ON CALL</h6>
<p data-font="sans" id="call-user-chip">Ayushya</p>
<p data-font="sans" id="call-user-chip" class="not-me-user-chip">Someone</p>
<p data-font="sans" id="call-user-chip" class="not-me-user-chip">Won't Stop Talking <input type="range" value="2" min="0" max="10"></p>
</div>
</div>
<div class="explanation">
<div>
<h4 data-font="sans-bold">MADE FOR YOU & YOUR GANG</h4>
<p data-font="sans">See who's in your party, get on a call with your party, and volume control for the person who just won't stop talking. </p>
</div>
</div>
</div>
</div>
<div id="tips" class="landing">
<p data-font="sans-bold" class="title">
TIPS & FAQ
</p>
<div class="tip">
<h6 data-font="sans">
DO I STILL NEED ACCOUNTS FOR THE STREAMING PLATFORMS
</h6>
<p data-font="sans-bold">
Yes, Watch Party is not a hack. The sole purpose of Watch Party is to help synchronize the playing/pausing on streamed content.
</p>
</div>
<div class="tip">
<h6 data-font="sans">
HOW CAN I HELP KEEP WATCH PARTY ALIVE
</h6>
<p data-font="sans-bold">
Watch Party is built to be completely free, all the costs are paid by the developer alone. While no payment is needed, any small donation helps make sure I can keep the servers up and running. You can request more details by reaching out to admin@chromewatchparty.com.
</p>
</div>
<div class="tip">
<h6 data-font="sans">
CAN I USE WATCH PARTY IN FULLSCREEN
@@ -87,14 +236,6 @@ Peacock TV logo is a trademark and intellectual property of Peacock TV & NBC Uni
If you started the party, you can simply join the party from a different place to change the party location.
</p>
</div>
<div class="tip">
<h6 data-font="sans">
I CAN'T START A CALL
</h6>
<p data-font="sans-bold">
A group call can only be started by the person who started the party - after that the rest of the party can join.
</p>
</div>
<div class="tip">
<h6 data-font="sans">
CHAT NOTIFICATIONS ARE ANNOYING
@@ -124,7 +265,7 @@ Peacock TV logo is a trademark and intellectual property of Peacock TV & NBC Uni
CAN I DISABLE WATCH PARTY TEMPORARILY
</h6>
<p data-font="sans-bold">
Watch Party is built to not be in your way but, it can be manually disabled by going to the Chrome Extensions page (chrome://extensions).</a>
Watch Party is built to not be in your way but, it can be manually disabled by going to the Chrome Extensions page (chrome://extensions).
</p>
</div>
<a data-font="sans-bold" href="mailto:admin@chromewatchparty.com?subject=New FAQ Question" class="link-button">