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", "name": "watch-party-crx",
"version": "1.6.0", "version": "1.6.0",
"private": true, "private": true,
"dependencies": { "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"
},
"scripts": { "scripts": {
"start": "node scripts/start.js", "start": "node scripts/start.js",
"build": "node scripts/build.js", "build": "node scripts/build.js",
@@ -101,6 +53,39 @@
"extends": "react-app" "extends": "react-app"
}, },
"devDependencies": { "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/core": "^7.1.6",
"@babel/preset-env": "^7.1.6", "@babel/preset-env": "^7.1.6",
"@babel/preset-react": "^7.0.0", "@babel/preset-react": "^7.0.0",
@@ -110,6 +95,19 @@
"eslint-plugin-react-hooks": "^2.0.1", "eslint-plugin-react-hooks": "^2.0.1",
"mini-css-extract-plugin": "^0.8.0", "mini-css-extract-plugin": "^0.8.0",
"webpack": "^4.42.1", "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 Written by Ayushya Amitabh <admin@chromewatchparty.com>, 2020
https://www.chromewatchparty.com/copyright 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 CITED ARE THE SOLE INTELECTUAL PROPERTY OF AYUSHYA AMITABH, AND MAY NOT BE
COPIED, EDITED, OR REPRODUCED WITHOUT PERMISSION. 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> <!DOCTYPE html>
<html> <html>
+69 -14
View File
@@ -5,20 +5,16 @@ Proprietary and confidential
Written by Ayushya Amitabh <admin@chromewatchparty.com>, 2020 Written by Ayushya Amitabh <admin@chromewatchparty.com>, 2020
https://www.chromewatchparty.com/copyright 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 CITED ARE THE SOLE INTELECTUAL PROPERTY OF AYUSHYA AMITABH, AND MAY NOT BE
COPIED, EDITED, OR REPRODUCED WITHOUT PERMISSION. 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 chrome */
/* global firebase */ /* global firebase */
const APP_VERSION = '1.6.0';
const APP_VERSION_STRING = APP_VERSION.replace(/\./g, '');
const firebaseConfig = { const firebaseConfig = {
apiKey: 'AIzaSyCJLuYpzXLDRmu2ttzFuSGmYcpFxqToWFU', apiKey: 'AIzaSyCJLuYpzXLDRmu2ttzFuSGmYcpFxqToWFU',
authDomain: 'watch-party-crx.firebaseapp.com', authDomain: 'watch-party-crx.firebaseapp.com',
@@ -68,11 +64,40 @@ const sendAuth = async (sendResponse) => {
sendResponse(firebase.auth().currentUser); sendResponse(firebase.auth().currentUser);
}; };
chrome.runtime.onInstalled.addListener(() => { chrome.runtime.onInstalled.addListener((details) => {
chrome.tabs.create({ if (details.reason) {
url: 'https://www.chromewatchparty.com/welcome', if (details.reason === 'install') {
active: true, 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) => { chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
@@ -84,6 +109,16 @@ chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
case 'GET_AUTH': case 'GET_AUTH':
sendAuth(sendResponse); sendAuth(sendResponse);
break; 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: default:
console.warn('Unknown message type: <', type, '>'); console.warn('Unknown message type: <', type, '>');
break; break;
@@ -98,7 +133,7 @@ chrome.runtime.onConnect.addListener((port) => {
port.messageListener && port.messageListener(); port.messageListener && port.messageListener();
}); });
port.onMessage.addListener((request) => { port.onMessage.addListener((request) => {
const { type, roomId } = request; const { type, roomId, roomPin } = request;
if (type === 'INIT') { if (type === 'INIT') {
port.roomListener = firebase port.roomListener = firebase
.firestore() .firestore()
@@ -152,6 +187,26 @@ chrome.runtime.onConnect.addListener((port) => {
port.postMessage({ isError: true, ...err }); 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 Written by Ayushya Amitabh <admin@chromewatchparty.com>, 2020
https://www.chromewatchparty.com/copyright 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 CITED ARE THE SOLE INTELECTUAL PROPERTY OF AYUSHYA AMITABH, AND MAY NOT BE
COPIED, EDITED, OR REPRODUCED WITHOUT PERMISSION. 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> <!DOCTYPE html>
<html> <html>
+6 -2
View File
@@ -17,7 +17,9 @@
"https://*.youtube.com/*", "https://*.youtube.com/*",
"https://play.hbonow.com/*", "https://play.hbonow.com/*",
"https://play.hbomax.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_security_policy": "script-src 'self' https://www.gstatic.com/ https://*.firebaseio.com https://www.googleapis.com; object-src 'self'",
"content_scripts": [ "content_scripts": [
@@ -30,7 +32,9 @@
"https://*.youtube.com/*", "https://*.youtube.com/*",
"https://play.hbonow.com/*", "https://play.hbonow.com/*",
"https://play.hbomax.com/*", "https://play.hbomax.com/*",
"https://*.peacocktv.com/*" "https://*.peacocktv.com/*",
"https://play.google.com/*",
"https://*.crunchyroll.com/*"
], ],
"run_at": "document_idle", "run_at": "document_idle",
"js": ["/static/js/content.js"], "js": ["/static/js/content.js"],
+24 -9
View File
@@ -5,20 +5,17 @@ Proprietary and confidential
Written by Ayushya Amitabh <admin@chromewatchparty.com>, 2020 Written by Ayushya Amitabh <admin@chromewatchparty.com>, 2020
https://www.chromewatchparty.com/copyright 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 CITED ARE THE SOLE INTELECTUAL PROPERTY OF AYUSHYA AMITABH, AND MAY NOT BE
COPIED, EDITED, OR REPRODUCED WITHOUT PERMISSION. 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> <!DOCTYPE html>
<html> <html>
<head> <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> <style>
a { a {
display: table; display: table;
@@ -34,6 +31,7 @@ HBO Now and HBO Max logos are trademark and intellectual property of WarnerMedia
cursor: pointer; cursor: pointer;
border-radius: 36px; border-radius: 36px;
text-decoration: none; text-decoration: none;
outline: none;
} }
p { p {
word-break: break-word; word-break: break-word;
@@ -50,15 +48,30 @@ HBO Now and HBO Max logos are trademark and intellectual property of WarnerMedia
background-color: #c0f300; background-color: #c0f300;
color: black; color: black;
} }
body { body {
min-width: max-content; min-width: max-content;
padding: 5px 10px; padding: 5px 10px;
background: #424242; 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> </style>
</head> </head>
<body> <body>
<script src="popup.js"></script>
<p> <p>
NOTHING TO DO HERE. GO TO A SUPPORTED WEBSITE, AND LOOK FOR WATCH PARTY IN THE BOTTOM RIGHT NOTHING TO DO HERE. GO TO A SUPPORTED WEBSITE, AND LOOK FOR WATCH PARTY IN THE BOTTOM RIGHT
CORNER OF YOUR SCREEN 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="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="mailto:admin@chromewatchparty.com?subject=User Feedback">Feedback</a>
<a target="_blank" href="https://paypal.me/Ayushya">Donate</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> </body>
</html> </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 Written by Ayushya Amitabh <admin@chromewatchparty.com>, 2020
https://www.chromewatchparty.com/copyright 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 CITED ARE THE SOLE INTELECTUAL PROPERTY OF AYUSHYA AMITABH, AND MAY NOT BE
COPIED, EDITED, OR REPRODUCED WITHOUT PERMISSION. 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 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 = { export const PLAYER_STATUS = {
PLAYING: 'PLAYING', PLAYING: 'PLAYING',
@@ -35,6 +28,8 @@ export const PLAYER_SOURCE = {
HBONOW: 'hbonow.', HBONOW: 'hbonow.',
HBOMAX: 'hbomax.', HBOMAX: 'hbomax.',
PEACOCK: 'peacocktv.', PEACOCK: 'peacocktv.',
GOOGLEPLAY: 'play.google.',
CRUNCHYROLL: 'crunchyroll.',
LOCAL: 'localhost', LOCAL: 'localhost',
}; };
@@ -48,6 +43,8 @@ export const getPlayerSource = () => {
if (HREF.includes(PLAYER_SOURCE.HBONOW)) return PLAYER_SOURCE.HBONOW; 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.HBOMAX)) return PLAYER_SOURCE.HBOMAX;
if (HREF.includes(PLAYER_SOURCE.PEACOCK)) return PLAYER_SOURCE.PEACOCK; 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; if (HREF.includes(PLAYER_SOURCE.LOCAL)) return PLAYER_SOURCE.LOCAL;
}; };
@@ -71,6 +68,13 @@ export const emptyLocalData = () => {
window.localStorage.removeItem('WatchParty'); 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 () => export const getUserUid = async () =>
new Promise((resolve, reject) => { new Promise((resolve, reject) => {
chrome.runtime.sendMessage( 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) => { export const CreateRoomApi = async (data) => {
return fetcher({ return fetcher({
endpoint: 'create', endpoint: 'create',
@@ -149,9 +170,9 @@ export const UpdatePlayerApi = async (data) => {
}); });
}; };
export const GetVoxeetTokenApi = async () => { export const GetVoxeetTokenApi = async (data) => {
return fetcher({ return fetcher({
endpoint: 'validator', endpoint: `validator/${data.partyName}`,
method: 'GET', method: 'GET',
}); });
}; };
@@ -170,3 +191,10 @@ export const UpdateVoxeetApi = async (data) => {
body: JSON.stringify(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 Written by Ayushya Amitabh <admin@chromewatchparty.com>, 2020
https://www.chromewatchparty.com/copyright 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 CITED ARE THE SOLE INTELECTUAL PROPERTY OF AYUSHYA AMITABH, AND MAY NOT BE
COPIED, EDITED, OR REPRODUCED WITHOUT PERMISSION. 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 React, { useState, useEffect } from 'react';
import Router from 'route-lite'; 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 Create from './Create';
import Loading from './Loading'; import Loading from './Loading';
import firebase from 'firebase/app'; import firebase from 'firebase/app';
@@ -47,6 +40,30 @@ const Routes = (props) => (
</Router> </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 App = () => {
const closedNotification = { open: false, message: '' }; const closedNotification = { open: false, message: '' };
@@ -114,6 +131,15 @@ const App = () => {
/> />
<div className="party-content-wrapper"> <div className="party-content-wrapper">
<div id="party-routes-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 />} {loading && <Loading />}
{status[APP_VERSION_STRING] && status[APP_VERSION_STRING].active === true ? ( {status[APP_VERSION_STRING] && status[APP_VERSION_STRING].active === true ? (
<Routes <Routes
@@ -121,13 +147,14 @@ const App = () => {
notify={(message) => setNotification({ open: true, message })} notify={(message) => setNotification({ open: true, message })}
appVersion={APP_VERSION} appVersion={APP_VERSION}
appVersionString={APP_VERSION_STRING} appVersionString={APP_VERSION_STRING}
status={status[APP_VERSION_STRING]}
/> />
) : ( ) : (
<Inactive <Inactive
status={ status={
status[APP_VERSION_STRING] status[APP_VERSION_STRING]
? status[APP_VERSION_STRING].message ? 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 Written by Ayushya Amitabh <admin@chromewatchparty.com>, 2020
https://www.chromewatchparty.com/copyright 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 CITED ARE THE SOLE INTELECTUAL PROPERTY OF AYUSHYA AMITABH, AND MAY NOT BE
COPIED, EDITED, OR REPRODUCED WITHOUT PERMISSION. 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 { import {
GetVoxeetTokenApi, GetVoxeetTokenApi,
GetRefreshedVoxeetTokenApi, GetRefreshedVoxeetTokenApi,
LocalData, LocalData,
UpdateVoxeetApi, UpdateVoxeetApi,
updateLocalData, updateLocalData,
} from './Api'; } from "./Api";
export default class CallManager { export default class CallManager {
constructor(listener, notify) { constructor(listener, notify) {
@@ -33,12 +26,18 @@ export default class CallManager {
this.__accessToken = LocalData().vrT; this.__accessToken = LocalData().vrT;
this.__sessionInitialized = false; this.__sessionInitialized = false;
this.__admin = false; this.__admin = false;
this.__adminOnly = true;
} }
toggleCall(onCall, conference, admin) { toggleCall(onCall, conference, admin, adminOnly) {
this.listener({ callActionPending: true }); try {
this.__admin = admin; this.listener({ callActionPending: true });
if (onCall === true) this.leaveCall(); this.__admin = admin;
else this.boot(conference); 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) { toggleMute(muted) {
this.listener({ callActionPending: true }); this.listener({ callActionPending: true });
@@ -53,16 +52,23 @@ export default class CallManager {
.fetch(conference._id) .fetch(conference._id)
.then(this.joinCall) .then(this.joinCall)
.catch((err) => { .catch((err) => {
console.error('Failed to fetch conference', err); console.error("Failed to fetch conference", err);
this.startCall(); this.startCall();
}); });
} else if (opened && !conference) this.startCall(); } else if (opened && !conference) {
else console.error('Unable to open Voxeet Session'); this.startCall();
} else {
this.listener({ onCall: false, muted: false, callActionPending: false });
console.error("Unable to open Voxeet Session");
}
} }
async startCall() { async startCall() {
if (this.__admin === true) { if (!this.__adminOnly || (this.__adminOnly && this.__admin)) {
VoxeetSDK.conference VoxeetSDK.conference
.create({ alias: LocalData().partyName, pin: LocalData().partyPin }) .create({
alias: `AUTODELETE_${LocalData().partyName}`,
pin: LocalData().partyPin,
})
.then(async (conference) => { .then(async (conference) => {
const updatedVoxeet = await UpdateVoxeetApi({ const updatedVoxeet = await UpdateVoxeetApi({
conference, conference,
@@ -72,25 +78,55 @@ export default class CallManager {
}) })
.then((res) => res.json()) .then((res) => res.json())
.then((res) => res.status <= 299) .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) { if (updatedVoxeet === true) {
this.joinCall(conference); this.joinCall(conference);
} else { } else {
console.error('Failed to update party Voxeet'); this.listener({
onCall: false,
muted: false,
callActionPending: false,
});
console.error("Failed to update party Voxeet");
} }
}); });
} else { } else {
this.notify('Ask your party admin to start the call.'); this.notify("Ask your party admin to start the call.");
this.leaveCall(); this.leaveCall();
} }
} }
async joinCall(conference) { async joinCall(conference) {
const joined = await VoxeetSDK.conference const joined = await VoxeetSDK.conference
.join(conference, { constraints: { audio: true, video: false } }) .join(conference, { constraints: { audio: true, video: false } })
.then(() => true); .then(() => true)
if (!joined) { .catch((e) => {
this.startCall(); 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() { async leaveCall() {
VoxeetSDK.conference.leave().then(() => { VoxeetSDK.conference.leave().then(() => {
@@ -107,9 +143,9 @@ export default class CallManager {
}); });
return true; return true;
} catch (error) { } 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 false;
} }
return true; return true;
@@ -122,33 +158,26 @@ export default class CallManager {
this.listener({ usersOnCall }); this.listener({ usersOnCall });
} }
async initialize() { async initialize() {
VoxeetSDK.conference.on('participantAdded', () => this.usersListHandle()); VoxeetSDK.conference.on("participantAdded", () => this.usersListHandle());
VoxeetSDK.conference.on('participantUpdated', () => this.usersListHandle()); VoxeetSDK.conference.on("participantUpdated", () => this.usersListHandle());
VoxeetSDK.conference.on('joined', () => { VoxeetSDK.conference.on("joined", () => {
this.listener({ onCall: true, callActionPending: false }); this.listener({ onCall: true, callActionPending: false });
}); });
VoxeetSDK.conference.on('left', () => { VoxeetSDK.conference.on("left", () => {
this.listener({ onCall: false, callActionPending: false, usersOnCall: {} }); this.listener({
onCall: false,
callActionPending: false,
usersOnCall: {},
});
}); });
VoxeetSDK.conference.on('streamAdded', (participant, stream) => { 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);
}
this.listener({ callActionPending: false }); this.listener({ callActionPending: false });
}); });
VoxeetSDK.conference.on('streamRemoved', (participant, stream) => { VoxeetSDK.conference.on("streamRemoved", (participant, stream) => {
if (document.getElementById(participant.id)) document.getElementById(participant.id).remove();
this.listener({ callActionPending: false }); this.listener({ callActionPending: false });
}); });
@@ -158,7 +187,7 @@ export default class CallManager {
this.__accessToken !== null || this.__accessToken !== null ||
this.__refreshToken !== null this.__refreshToken !== null
) { ) {
await GetVoxeetTokenApi() await GetVoxeetTokenApi({ partyName: LocalData().partyName })
.then((res) => res.json()) .then((res) => res.json())
.then((tokenResult) => { .then((tokenResult) => {
if (tokenResult.status <= 299) { if (tokenResult.status <= 299) {
@@ -166,11 +195,11 @@ export default class CallManager {
this.updateTokens(access_token, refresh_token); this.updateTokens(access_token, refresh_token);
VoxeetSDK.initializeToken(access_token, refresh_token); VoxeetSDK.initializeToken(access_token, refresh_token);
} else { } else {
console.error('Get Voxeet Token API Result Error', tokenResult); console.error("Get Voxeet Token API Result Error", tokenResult);
} }
}) })
.catch((err) => { .catch((err) => {
console.error('Get Voxeet Token API error', err); console.error("Get Voxeet Token API error", err);
}); });
} else { } else {
VoxeetSDK.initializeToken(this.__accessToken, this.__refreshToken); VoxeetSDK.initializeToken(this.__accessToken, this.__refreshToken);
@@ -186,7 +215,7 @@ export default class CallManager {
resolve(access_token); resolve(access_token);
}) })
.catch((err) => { .catch((err) => {
console.error('Get Voxeet Token API error', err); console.error("Get Voxeet Token API error", err);
reject(err); reject(err);
}); });
}); });
+271 -72
View File
@@ -5,16 +5,9 @@ Proprietary and confidential
Written by Ayushya Amitabh <admin@chromewatchparty.com>, 2020 Written by Ayushya Amitabh <admin@chromewatchparty.com>, 2020
https://www.chromewatchparty.com/copyright 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 CITED ARE THE SOLE INTELECTUAL PROPERTY OF AYUSHYA AMITABH, AND MAY NOT BE
COPIED, EDITED, OR REPRODUCED WITHOUT PERMISSION. 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 chrome */
import React, { Component } from 'react'; import React, { Component } from 'react';
@@ -35,10 +28,11 @@ import {
FormControlLabel, FormControlLabel,
Chip, Chip,
CircularProgress, CircularProgress,
Paper,
Grid,
Slider,
} from '@material-ui/core'; } from '@material-ui/core';
import Thread from './Thread'; import Thread from './Thread';
import { Carousel } from '@giphy/react-components';
import { GiphyFetch } from '@giphy/js-fetch-api';
import { import {
SendMessageApi, SendMessageApi,
LeaveRoomApi, LeaveRoomApi,
@@ -48,8 +42,10 @@ import {
PLAYER_SOURCE, PLAYER_SOURCE,
LocalData, LocalData,
PLAYER_STATUS, PLAYER_STATUS,
emptyLocalData,
updateLocalData, updateLocalData,
UpdateAdminOnly,
scrollThread,
removeLocalData,
} from './Api'; } from './Api';
import { import {
Send, Send,
@@ -69,24 +65,17 @@ import {
Error, Error,
FullscreenExit, FullscreenExit,
Fullscreen, Fullscreen,
VolumeDown,
VolumeUp,
CancelRounded,
} from '@material-ui/icons'; } from '@material-ui/icons';
import Create from './Create'; import Create from './Create';
import { goTo } from 'route-lite'; import { goTo } from 'route-lite';
import CallManager from './CallManager'; import CallManager from './CallManager';
import VideoListener from './VideoListener'; import VideoListener from './VideoListener';
import ReactGiphySearchbox from 'react-giphy-searchbox';
const giphyFetch = new GiphyFetch('ojJbIMUN6XRMn4mHiFFav3rMCpXUg8sq'); // 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.',
},
];
export default class Chat extends Component { export default class Chat extends Component {
constructor(props) { constructor(props) {
super(props); super(props);
@@ -95,6 +84,8 @@ export default class Chat extends Component {
messages: [], messages: [],
newMessage: '', newMessage: '',
admin: null, admin: null,
adminOnly: false,
adminModeUpdate: false,
users: [], users: [],
usersOnCall: {}, usersOnCall: {},
gif: false, gif: false,
@@ -107,6 +98,9 @@ export default class Chat extends Component {
isFullscreen: document.fullscreen, isFullscreen: document.fullscreen,
allowAutoRejoin: allowAutoRejoin:
LocalData().allowAutoRejoin !== undefined ? LocalData().allowAutoRejoin : true, LocalData().allowAutoRejoin !== undefined ? LocalData().allowAutoRejoin : true,
replyTo: null,
sendingMessages: [],
volumes: {},
}; };
this.port = null; this.port = null;
this.gifClick = this.gifClick.bind(this); this.gifClick = this.gifClick.bind(this);
@@ -115,9 +109,11 @@ export default class Chat extends Component {
this.leaveParty = this.leaveParty.bind(this); this.leaveParty = this.leaveParty.bind(this);
this.shareParty = this.shareParty.bind(this); this.shareParty = this.shareParty.bind(this);
this.syncPlayer = this.syncPlayer.bind(this); this.syncPlayer = this.syncPlayer.bind(this);
this.setReplyTo = this.setReplyTo.bind(this);
this.sendMessage = this.sendMessage.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.handlePhoneCall = this.handlePhoneCall.bind(this);
this.getReplyToContent = this.getReplyToContent.bind(this);
this.callEventListener = this.callEventListener.bind(this); this.callEventListener = this.callEventListener.bind(this);
this.sendMessageInvoker = this.sendMessageInvoker.bind(this); this.sendMessageInvoker = this.sendMessageInvoker.bind(this);
this.videoEventListener = this.videoEventListener.bind(this); this.videoEventListener = this.videoEventListener.bind(this);
@@ -167,6 +163,7 @@ export default class Chat extends Component {
this.port.postMessage({ this.port.postMessage({
type: 'INIT', type: 'INIT',
roomId: LocalData().partyName, roomId: LocalData().partyName,
roomPin: LocalData().partyPin,
}); });
// Listener // Listener
this.port.onMessage.addListener((response) => { this.port.onMessage.addListener((response) => {
@@ -175,22 +172,38 @@ export default class Chat extends Component {
goTo(Create, this.props); goTo(Create, this.props);
return; return;
} }
if (response.messages) {
scrollThread();
}
// IF CHAT IS HIDDEN AND ALLOWS NOTIFICATION // IF CHAT IS HIDDEN AND ALLOWS NOTIFICATION
if (!document.querySelector('body.visible') && this.state.allowChatNotification === true) { if (!document.querySelector('body.visible') && this.state.allowChatNotification === true) {
if (response.messages && response.messages.length !== this.state.messages.length) { if (response.messages && response.messages.length !== this.state.messages.length) {
const lastMessage = response.messages[response.messages.length - 1]; const lastMessage = response.messages[response.messages.length - 1];
var notificationMessage = ''; if (lastMessage.uid !== '<system>') {
if (lastMessage.uid !== '<system>') notificationMessage += lastMessage.name + ': '; this.props.notify(
notificationMessage += lastMessage.text; `${lastMessage.name}${
this.props.notify(notificationMessage); 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); this.setState(response);
if (response.player) this.mVideoListener.doUpdate(response.player); 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 // DONE WITH MOUNTING TASKS
this.props.updateLoading(false); this.props.updateLoading(false);
} }
@@ -199,7 +212,10 @@ export default class Chat extends Component {
this.leaveParty(); this.leaveParty();
} }
videoEventListener(video) { videoEventListener(video) {
this.syncPlayer(video); const { adminOnly, admin } = this.state;
if (!adminOnly || (adminOnly && admin)) {
this.syncPlayer(video);
}
} }
callEventListener(event) { callEventListener(event) {
this.setState(event); this.setState(event);
@@ -234,10 +250,18 @@ export default class Chat extends Component {
const data = { const data = {
partyName: LocalData().partyName, partyName: LocalData().partyName,
}; };
if (this.state.onCall)
this.mCallManager.toggleCall(
this.state.onCall,
this.state.conference,
this.state.admin,
this.state.adminOnly
);
DeleteRoomApi(data) DeleteRoomApi(data)
.then((res) => { .then((res) => {
this.props.notify('Ended party'); this.props.notify('Ended party');
emptyLocalData(); removeLocalData('partyPin');
removeLocalData('partyName');
goTo(Create, this.props); goTo(Create, this.props);
this.props.updateLoading(false); this.props.updateLoading(false);
}) })
@@ -254,10 +278,18 @@ export default class Chat extends Component {
partyName: LocalData().partyName, partyName: LocalData().partyName,
userUid: LocalData().userUid, userUid: LocalData().userUid,
}; };
if (this.state.onCall)
this.mCallManager.toggleCall(
this.state.onCall,
this.state.conference,
this.state.admin,
this.state.adminOnly
);
LeaveRoomApi(data) LeaveRoomApi(data)
.then((res) => { .then((res) => {
this.props.notify('Left party'); this.props.notify('Left party');
emptyLocalData(); removeLocalData('partyPin');
removeLocalData('partyName');
goTo(Create, this.props); goTo(Create, this.props);
this.props.updateLoading(false); this.props.updateLoading(false);
}) })
@@ -279,7 +311,9 @@ export default class Chat extends Component {
this.props.notify('Copied party invitation'); this.props.notify('Copied party invitation');
} }
sendMessage(message) { sendMessage(message) {
const { sendingMessages } = this.state;
const data = { const data = {
id: `sending-${sendingMessages.length}`,
roomId: LocalData().partyName, roomId: LocalData().partyName,
name: LocalData().userName, name: LocalData().userName,
uid: LocalData().userUid, uid: LocalData().userUid,
@@ -288,10 +322,30 @@ export default class Chat extends Component {
type: '', type: '',
}; };
if (this.state.gif === true) data.type = 'giphy'; if (this.state.gif === true) data.type = 'giphy';
SendMessageApi(data).catch((err) => { if (this.state.replyTo !== null) data.replyTo = this.state.replyTo.id;
this.props.notify(err.message || err.statusText); sendingMessages.push(data);
console.error('Send Message API error', err); 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) { sendMessageInvoker(overrideMessage) {
if (overrideMessage) this.sendMessage(overrideMessage); if (overrideMessage) this.sendMessage(overrideMessage);
@@ -301,17 +355,11 @@ export default class Chat extends Component {
} }
this.setState({ newMessage: '' }); this.setState({ newMessage: '' });
} }
gifClick(iGif, ev) { gifClick(iGif) {
ev.preventDefault();
this.sendMessageInvoker(iGif.images.original.url); 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() { handlePhoneCall() {
const { onCall, conference, admin } = this.state; const { onCall, conference, admin, adminOnly } = this.state;
if (window.navigator && onCall === false) { if (window.navigator && onCall === false) {
try { try {
const overlay = document.createElement('div'); const overlay = document.createElement('div');
@@ -327,7 +375,7 @@ export default class Chat extends Component {
}) })
.then(() => { .then(() => {
document.getElementById('mic-permission-overlay').remove(); document.getElementById('mic-permission-overlay').remove();
this.mCallManager.toggleCall(onCall, conference, admin); this.mCallManager.toggleCall(onCall, conference, admin, adminOnly);
}) })
.catch(() => { .catch(() => {
document.getElementById('mic-permission-overlay').remove(); document.getElementById('mic-permission-overlay').remove();
@@ -336,9 +384,9 @@ export default class Chat extends Component {
); );
}); });
} catch (err) { } 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() { handleMute() {
this.mCallManager.toggleMute(this.state.muted); this.mCallManager.toggleMute(this.state.muted);
@@ -363,6 +411,49 @@ export default class Chat extends Component {
document.documentElement.requestFullscreen(); 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() { render() {
const { const {
admin, admin,
@@ -379,7 +470,31 @@ export default class Chat extends Component {
allowAutoRejoin, allowAutoRejoin,
usersOnCall, usersOnCall,
isFullscreen, isFullscreen,
adminModeUpdate,
adminOnly,
replyTo,
sendingMessages,
volumes,
} = this.state; } = 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 ( return (
<div className="party-chat-container"> <div className="party-chat-container">
<div className="party-chat-header"> <div className="party-chat-header">
@@ -496,6 +611,32 @@ export default class Chat extends Component {
} }
labelPlacement="end" 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"> <Typography className="party-controls-users-title" variant="overline">
PARTY MEMBERS PARTY MEMBERS
</Typography> </Typography>
@@ -514,17 +655,41 @@ export default class Chat extends Component {
{Object.keys(usersOnCall).map((id) => { {Object.keys(usersOnCall).map((id) => {
return usersOnCall[id].status === 'Connected' || return usersOnCall[id].status === 'Connected' ||
usersOnCall[id].status === 'Connecting' ? ( usersOnCall[id].status === 'Connecting' ? (
<Chip <Tooltip
color={ interactive
usersOnCall[id].info.externalId === LocalData().userUid title={
? 'secondary' <Grid className="party-controls-users-volume" container spacing={2}>
: 'default' <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 <Chip
size="small" color={
label={usersOnCall[id].info.name || ' '} usersOnCall[id].info.externalId === LocalData().userUid
/> ? 'secondary'
: 'default'
}
className="party-controls-users-chip"
clickable
size="small"
label={usersOnCall[id].info.name || ' '}
/>
</Tooltip>
) : null; ) : null;
})} })}
</div> </div>
@@ -570,7 +735,7 @@ export default class Chat extends Component {
</Card> </Card>
) : null} ) : null}
</div> </div>
<div className="thread-container"> <div className="thread-wrapper">
<div className="alerts-container"> <div className="alerts-container">
{alerts.map((a) => { {alerts.map((a) => {
if (a.condition() === true) { if (a.condition() === true) {
@@ -585,8 +750,27 @@ export default class Chat extends Component {
} else return null; } else return null;
})} })}
</div> </div>
<Thread messages={messages} /> <Thread
setReplyTo={this.setReplyTo}
sendingMessages={sendingMessages}
sentMessages={messages}
/>
</div> </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"> <div className="text-phone-panel">
{gif || newMessage ? null : ( {gif || newMessage ? null : (
<Tooltip title="Fullscreen with Watch Party" aria-label="Fullscreen"> <Tooltip title="Fullscreen with Watch Party" aria-label="Fullscreen">
@@ -656,9 +840,13 @@ export default class Chat extends Component {
value={newMessage} value={newMessage}
onChange={(e) => { onChange={(e) => {
this.setState({ newMessage: e.target.value }); this.setState({ newMessage: e.target.value });
if (this.state.gif) { scrollThread();
const carousel = document.querySelector('.giphy-carousel'); if (gif) {
setTimeout(() => carousel.scroll(carousel.scrollWidth, carousel.scrollHeight), 250); window.postMessage({
watchPartyDirection: 'TO_SCRIPT',
type: 'WATCH_PARTY_SEARCH_GIF',
value: e.target.value,
});
} }
}} }}
InputProps={{ InputProps={{
@@ -681,7 +869,17 @@ export default class Chat extends Component {
<InputAdornment position="start"> <InputAdornment position="start">
<IconButton <IconButton
className={`gif-button ${this.state.gif ? 'gif-close' : ''}`} 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" edge="start"
> >
{gif ? <Cancel /> : <Gif />} {gif ? <Cancel /> : <Gif />}
@@ -691,15 +889,16 @@ export default class Chat extends Component {
}} }}
/> />
</div> </div>
{gif ? ( <ReactGiphySearchbox
<Carousel apiKey="ojJbIMUN6XRMn4mHiFFav3rMCpXUg8sq"
fetchGifs={this.getCarousel} onSelect={this.gifClick}
gifHeight={200} masonryConfig={[{ columns: 2, imageWidth: '50%', gutter: 5 }]}
gutter={6} searchFormClassName="giphy-search-form"
onGifClick={this.gifClick} wrapperClassName={`giphy-wrapper ${gif ? '' : 'giphy-wrapper-down'}`}
hideAttribution listWrapperClassName="giphy-list-wrapper"
/> listItemClassName="giphy-list-item"
) : null} poweredByGiphy
/>
</div> </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 Written by Ayushya Amitabh <admin@chromewatchparty.com>, 2020
https://www.chromewatchparty.com/copyright 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 CITED ARE THE SOLE INTELECTUAL PROPERTY OF AYUSHYA AMITABH, AND MAY NOT BE
COPIED, EDITED, OR REPRODUCED WITHOUT PERMISSION. 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 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 { import {
JoinRoomApi, JoinRoomApi,
CreateRoomApi, CreateRoomApi,
@@ -26,6 +36,7 @@ import {
LocalData, LocalData,
updateLocalData, updateLocalData,
getUserUid, getUserUid,
emptyLocalData,
} from './Api'; } from './Api';
import { goTo } from 'route-lite'; import { goTo } from 'route-lite';
import Chat from './Chat'; import Chat from './Chat';
@@ -34,7 +45,13 @@ import {
InfoRounded, InfoRounded,
MonetizationOnRounded, MonetizationOnRounded,
RateReviewRounded, RateReviewRounded,
Settings,
ExpandLess,
KeyboardArrowLeft,
KeyboardArrowRight,
Notifications,
} from '@material-ui/icons'; } from '@material-ui/icons';
import { Autocomplete } from '@material-ui/lab';
const CONSTANTS = { const CONSTANTS = {
USER_NAME_LENGTH: 12, USER_NAME_LENGTH: 12,
@@ -50,10 +67,26 @@ export default class Create extends Component {
name: LocalData().partyName || '', name: LocalData().partyName || '',
pin: LocalData().partyPin || '', pin: LocalData().partyPin || '',
mode: defaultMode, 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.createCall = this.createCall.bind(this);
this.joinCall = this.joinCall.bind(this); this.joinCall = this.joinCall.bind(this);
this.success = this.success.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() { componentDidMount() {
const { lastActive, userName, partyName, partyPin, allowAutoRejoin } = LocalData(); const { lastActive, userName, partyName, partyPin, allowAutoRejoin } = LocalData();
@@ -63,6 +96,14 @@ export default class Create extends Component {
this.joinCall(userName, partyName, partyPin); this.joinCall(userName, partyName, partyPin);
} }
} }
chrome.runtime.sendMessage(
{
type: 'GET_PARTIES',
},
(response) => {
this.setState({ parties: response });
}
);
} }
partyNameChange(value) { partyNameChange(value) {
if (value.length <= CONSTANTS.NAME_LENGTH) this.setState({ name: value }); if (value.length <= CONSTANTS.NAME_LENGTH) this.setState({ name: value });
@@ -121,6 +162,7 @@ export default class Create extends Component {
time: 0, time: 0,
url: window.location.href, url: window.location.href,
}, },
adminOnly: this.state.adminOnly,
}; };
CreateRoomApi(apidata) CreateRoomApi(apidata)
.then((res) => res.json()) .then((res) => res.json())
@@ -157,6 +199,7 @@ export default class Create extends Component {
} }
}) })
.catch((err) => { .catch((err) => {
emptyLocalData();
console.error('Join Party API Error', err); console.error('Join Party API Error', err);
this.props.notify(err.message || 'Something went wrong'); this.props.notify(err.message || 'Something went wrong');
this.props.updateLoading(false); this.props.updateLoading(false);
@@ -167,8 +210,58 @@ export default class Create extends Component {
this.props.updateLoading(false); this.props.updateLoading(false);
goTo(Chat, this.props); 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() { 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 ( return (
<div className="party-create-container"> <div className="party-create-container">
<Typography <Typography
@@ -213,15 +306,26 @@ export default class Create extends Component {
userName ? `${userName.length}/${CONSTANTS.USER_NAME_LENGTH} Characters` : null userName ? `${userName.length}/${CONSTANTS.USER_NAME_LENGTH} Characters` : null
} }
/> />
<TextField <Autocomplete
color="secondary" color="secondary"
size="small" options={parties}
className="create-name addFocus" getOptionLabel={(o) => o.name}
label="Party Name" autoComplete
variant="outlined" freeSolo
value={name} value={{ name: name, pin: pin }}
onChange={(e) => this.partyNameChange(e.target.value)} onInputChange={(e, v, r) => this.partyNameAutocompleteProcessor({ name: v, pin: pin })}
helperText={name ? `${name.length}/${CONSTANTS.NAME_LENGTH} Characters` : null} 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 <TextField
color="secondary" color="secondary"
@@ -234,6 +338,31 @@ export default class Create extends Component {
onChange={(e) => this.partyPinChange(e.target.value)} onChange={(e) => this.partyPinChange(e.target.value)}
helperText={pin ? `${pin.length}/${CONSTANTS.PIN_LENGTH} Digits` : null} 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 <Button
size="small" size="small"
variant="contained" variant="contained"
@@ -243,13 +372,133 @@ export default class Create extends Component {
<span>{mode === 'create' ? 'Start Party' : 'Join Party'}</span> <ChevronRight /> <span>{mode === 'create' ? 'Start Party' : 'Join Party'}</span> <ChevronRight />
</Button> </Button>
</Paper> </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 <Button
target="_blank" target="_blank"
href={`http://www.chromewatchparty.com/changes/v${this.props.appVersion}`} href={`http://www.chromewatchparty.com/changes/v${this.props.appVersion}`}
fullWidth fullWidth
color="secondary" color="secondary"
variant="outlined" variant="contained"
size="large" size="small"
className="whats-new-button" className="whats-new-button"
> >
See what's new in V{this.props.appVersion} 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 Written by Ayushya Amitabh <admin@chromewatchparty.com>, 2020
https://www.chromewatchparty.com/copyright 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 CITED ARE THE SOLE INTELECTUAL PROPERTY OF AYUSHYA AMITABH, AND MAY NOT BE
COPIED, EDITED, OR REPRODUCED WITHOUT PERMISSION. 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 React from 'react';
import { Typography } from '@material-ui/core'; 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 Written by Ayushya Amitabh <admin@chromewatchparty.com>, 2020
https://www.chromewatchparty.com/copyright 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 CITED ARE THE SOLE INTELECTUAL PROPERTY OF AYUSHYA AMITABH, AND MAY NOT BE
COPIED, EDITED, OR REPRODUCED WITHOUT PERMISSION. 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 React from 'react';
import { CircularProgress } from '@material-ui/core'; import { CircularProgress } from '@material-ui/core';
const Loading = () => ( const Loading = () => (
<div id="party-loading"> <div id="party-loading">
<CircularProgress /> <CircularProgress color="secondary" />
</div> </div>
); );
+262 -90
View File
@@ -5,106 +5,278 @@ Proprietary and confidential
Written by Ayushya Amitabh <admin@chromewatchparty.com>, 2020 Written by Ayushya Amitabh <admin@chromewatchparty.com>, 2020
https://www.chromewatchparty.com/copyright 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 CITED ARE THE SOLE INTELECTUAL PROPERTY OF AYUSHYA AMITABH, AND MAY NOT BE
COPIED, EDITED, OR REPRODUCED WITHOUT PERMISSION. 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 { .thread-item {
width: 100%; color: white;
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;
width: fit-content; width: fit-content;
text-align: right; margin: 10px 0;
margin-left: auto !important; width: 100%;
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;
transition: all ease-in-out 0.3s; transition: all ease-in-out 0.3s;
} }
.party-message ~ .time { .thread-item.highlight .thread-message img,
right: 0; .thread-item.highlight .thread-message p {
border: 2px solid #c0f300;
} }
.party-message:hover ~ .time { .thread-item.highlight .thread-message img {
opacity: 1; 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; 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; 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 Written by Ayushya Amitabh <admin@chromewatchparty.com>, 2020
https://www.chromewatchparty.com/copyright 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 CITED ARE THE SOLE INTELECTUAL PROPERTY OF AYUSHYA AMITABH, AND MAY NOT BE
COPIED, EDITED, OR REPRODUCED WITHOUT PERMISSION. 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 React, { Component } from 'react';
import { Typography } from '@material-ui/core'; import {
Typography,
IconButton,
Paper,
TextField,
InputAdornment,
Button,
} from '@material-ui/core';
import { Reply, Send } from '@material-ui/icons';
import './Thread.css'; import './Thread.css';
import { LocalData } from './Api'; import { LocalData, webhookFetcher } from './Api';
const Thread = (props) => export default class Thread extends Component {
props.messages.map((m, midx) => { constructor(props) {
var { userUid } = LocalData(); super(props);
if (m.uid === '<system>') { this.getTimeString = this.getTimeString.bind(this);
// SYSTEM MESSAGES 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 ( return (
<Typography variant="body2" className="thread-item system-message" key={m.id} disabled> <Paper elevation={0} className="thread-interactive-text">
{m.text} <Typography className="thread-interactive-question" variant="overline">
</Typography> {msg.text}
);
} 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}
</Typography> </Typography>
{m.type === 'giphy' ? ( <TextField
<img className="party-message" alt="giphy gif" src={m.text} /> className="thread-interactive-answer addFocus"
) : ( size="small"
<Typography variant="body1" className="party-message"> id={`reply-${msg.id}`}
{m.text} multiline
</Typography> variant="outlined"
)} color="secondary"
<span className="time">{new Date(m.time.seconds * 1000).toLocaleTimeString()}</span> 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> </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 Written by Ayushya Amitabh <admin@chromewatchparty.com>, 2020
https://www.chromewatchparty.com/copyright 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 CITED ARE THE SOLE INTELECTUAL PROPERTY OF AYUSHYA AMITABH, AND MAY NOT BE
COPIED, EDITED, OR REPRODUCED WITHOUT PERMISSION. 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 { PLAYER_STATUS, getPlayerSource, PLAYER_SOURCE, LocalData } from './Api';
import simulate from './ClickSimulator';
const templateListener = (video) => console.log('MISSING VIDEO LISTENER'); const templateListener = (video) => console.log('MISSING VIDEO LISTENER');
@@ -39,6 +31,7 @@ export default class VideoListener {
this.onpause = this.onpause.bind(this); this.onpause = this.onpause.bind(this);
this.onplay = this.onplay.bind(this); this.onplay = this.onplay.bind(this);
this.shouldPushSync = this.shouldPushSync.bind(this); this.shouldPushSync = this.shouldPushSync.bind(this);
this.crunchyRollListener = this.crunchyRollListener.bind(this);
} }
shouldPushSync() { shouldPushSync() {
return ( return (
@@ -73,8 +66,13 @@ export default class VideoListener {
this.isUpdating = true; this.isUpdating = true;
if (this.video.element) { if (this.video.element) {
// REMOVE LISTENER BEFORE SYNCING // REMOVE LISTENER BEFORE SYNCING
this.video.element.removeEventListener('playing', this.onplay); switch (this.video.source) {
this.video.element.removeEventListener('pause', this.onpause); 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); await this.timeout(250);
// SEEK // SEEK
if (Math.abs(serverPlayer.time - this.video.element.currentTime) > 1.5) { if (Math.abs(serverPlayer.time - this.video.element.currentTime) > 1.5) {
@@ -87,6 +85,7 @@ export default class VideoListener {
await this.video.element await this.video.element
.play() .play()
.catch((err) => console.error('WatchParty erred when syncing time.')); .catch((err) => console.error('WatchParty erred when syncing time.'));
await this.timeout(250);
this.video.element.currentTime = serverPlayer.time; this.video.element.currentTime = serverPlayer.time;
await this.timeout(500); await this.timeout(500);
break; break;
@@ -105,14 +104,27 @@ export default class VideoListener {
this.video.element.currentTime = serverPlayer.time; this.video.element.currentTime = serverPlayer.time;
break; break;
case PLAYER_SOURCE.PEACOCK: case PLAYER_SOURCE.PEACOCK:
const sliderHead = document.body.querySelector( window.postMessage(
'.playback-scrubber-bar .rc-slider-handle' 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; break;
default: default:
break; break;
@@ -126,9 +138,23 @@ export default class VideoListener {
PLAYER_SOURCE.NETFLIX === this.video.source || PLAYER_SOURCE.NETFLIX === this.video.source ||
PLAYER_SOURCE.HBOMAX === this.video.source || PLAYER_SOURCE.HBOMAX === this.video.source ||
PLAYER_SOURCE.HBONOW === this.video.source PLAYER_SOURCE.HBONOW === this.video.source
) ) {
window.postMessage({ type: 'PAUSE', time: serverPlayer.time }, '*'); 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; break;
case PLAYER_STATUS.PLAYING: case PLAYER_STATUS.PLAYING:
if ( if (
@@ -136,20 +162,38 @@ export default class VideoListener {
PLAYER_SOURCE.NETFLIX === this.video.source || PLAYER_SOURCE.NETFLIX === this.video.source ||
PLAYER_SOURCE.HBOMAX === this.video.source || PLAYER_SOURCE.HBOMAX === this.video.source ||
PLAYER_SOURCE.HBONOW === this.video.source PLAYER_SOURCE.HBONOW === this.video.source
) ) {
window.postMessage({ type: 'PLAY', time: serverPlayer.time }, '*'); 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 await this.video.element
.play() .play()
.catch((err) => console.error('WatchParty erred when syncing play.')); .catch((err) => console.error('WatchParty erred when syncing play.'));
}
break; break;
default: default:
break; break;
} }
await this.timeout(500); await this.timeout(500);
// RE-ADD LISTENERS FOR FUTURE SYNC // RE-ADD LISTENERS FOR FUTURE SYNC
this.video.element.addEventListener('playing', this.onplay); switch (this.video.source) {
this.video.element.addEventListener('pause', this.onpause); 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; this.isUpdating = false;
} }
@@ -218,6 +262,20 @@ export default class VideoListener {
return peacockshow === '' && peacockepisode === '' return peacockshow === '' && peacockepisode === ''
? null ? null
: peacockshow + '\n' + peacockepisode; : 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: default:
break; break;
} }
@@ -236,21 +294,39 @@ export default class VideoListener {
showName: await this.getShowName(), 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) { async firstTimeSync(videoElement) {
this.updateStatus({ this.updateStatus({
element: videoElement, element: videoElement,
}); });
// if (videoElement.currentTime > 0 && !videoElement.paused) {
// this.updateStatus({
// time: videoElement.currentTime,
// status: PLAYER_STATUS.PLAYING,
// });
// }
this.updateStatus({ this.updateStatus({
showName: await this.getShowName(), showName: await this.getShowName(),
}); });
videoElement.addEventListener('playing', this.onplay); switch (this.video.source) {
videoElement.addEventListener('pause', this.onpause); 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() { async start() {
this.updateStatus({ source: getPlayerSource(), showName: await this.getShowName() }); this.updateStatus({ source: getPlayerSource(), showName: await this.getShowName() });
@@ -271,9 +347,11 @@ export default class VideoListener {
this.video.element this.video.element
? null ? null
: () => { : () => {
var nve = document var nve = document.getElementsByClassName('AkiraPlayer')[0]
.getElementsByClassName('AkiraPlayer')[0] ? document
.getElementsByTagName('video')[0]; .getElementsByClassName('AkiraPlayer')[0]
.getElementsByTagName('video')[0]
: null;
if ( if (
nve !== null && nve !== null &&
(this.video.element !== nve || this.video.element === null) (this.video.element !== nve || this.video.element === null)
@@ -289,9 +367,11 @@ export default class VideoListener {
this.video.element this.video.element
? null ? null
: () => { : () => {
var nve = document var nve = document.getElementsByClassName('AkiraPlayer')[0]
.getElementsByClassName('AkiraPlayer')[0] ? document
.getElementsByTagName('video')[0]; .getElementsByClassName('AkiraPlayer')[0]
.getElementsByTagName('video')[0]
: null;
if (nve !== null && (this.video.element !== nve || this.video.element === null)) { if (nve !== null && (this.video.element !== nve || this.video.element === null)) {
this.firstTimeSync(nve); this.firstTimeSync(nve);
} }
@@ -430,8 +510,12 @@ export default class VideoListener {
} }
break; break;
case PLAYER_SOURCE.PEACOCK: 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( 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) { if (peacockVideoElement) {
this.firstTimeSync(peacockVideoElement); this.firstTimeSync(peacockVideoElement);
@@ -441,7 +525,7 @@ export default class VideoListener {
? null ? null
: () => { : () => {
var pve = document.querySelector( 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 ( if (
pve !== null && pve !== null &&
@@ -455,6 +539,55 @@ export default class VideoListener {
); );
} }
break; 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: case PLAYER_SOURCE.LOCAL:
const localVideoElement = document.getElementsByTagName('video')[0]; const localVideoElement = document.getElementsByTagName('video')[0];
if (localVideoElement) { if (localVideoElement) {
@@ -466,9 +599,15 @@ export default class VideoListener {
} }
} }
end() { end() {
if (this.video.element) { switch (this.video.source) {
this.video.element.removeEventListener('playing', this.onplay); case PLAYER_SOURCE.CRUNCHYROLL:
this.video.element.removeEventListener('pause', this.onpause); 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 Written by Ayushya Amitabh <admin@chromewatchparty.com>, 2020
https://www.chromewatchparty.com/copyright 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 CITED ARE THE SOLE INTELECTUAL PROPERTY OF AYUSHYA AMITABH, AND MAY NOT BE
COPIED, EDITED, OR REPRODUCED WITHOUT PERMISSION. 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. HBO Now and HBO Max logos are trademark and intellectual property of WarnerMedia Direct,LLC.
************************************************************************/ ************************************************************************/
:root { :root {
--party-width: 400px; --party-width: 425px;
--neg-party-width: -400px; --neg-party-width: -425px;
--body-width: calc(100% - var(--party-width)); --body-width: calc(100% - var(--party-width));
} }
body { body {
@@ -35,7 +35,7 @@ body.visible {
body.visible .watch-party-fab { body.visible .watch-party-fab {
right: var(--party-width) !important; right: var(--party-width) !important;
transform: translateX(0); transform: translateX(0);
top: 1.5rem; top: 22.5px !important;
border-top-right-radius: 0; border-top-right-radius: 0;
border-bottom-right-radius: 0; border-bottom-right-radius: 0;
background-color: #424242 !important; background-color: #424242 !important;
@@ -47,12 +47,35 @@ body.visible .party-notification-container {
max-width: var(--party-width); 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.netflix.visible .AkiraPlayer, /* NETFLIX */
body.hulu.visible .Player__container,/* HULU */ body.hulu.visible .Player__container,/* HULU */
body.amazon-prime.visible #dv-web-player.dv-player-fullscreen, /* AMAZON PRIME */ body.amazon-prime.visible #dv-web-player.dv-player-fullscreen, /* AMAZON PRIME */
body.disney.visible #hudson-wrapper, /* DISNEY */ body.disney.visible #hudson-wrapper, /* DISNEY */
body.youtube.visible #content.ytd-app, /* YOUTUBE */ 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; max-width: var(--body-width) !important;
} }
/* HULU MINIMIZED VIDEO */ /* HULU MINIMIZED VIDEO */
@@ -130,16 +153,79 @@ body.peacock.visible .playback-header__container {
position: relative; position: relative;
max-width: 100%; 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;
}
/* body.hulu #party_wrapper *,
FULL SCREEN BUTTON REMOVER body.peacock #party_wrapper *,
*/ body.disney #party_wrapper * {
body.netflix box-sizing: inherit;
.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;
} }
#party-loading { #party-loading {
@@ -179,13 +265,13 @@ body.amazon .fyysciv.f1lobqlo.fk0j80b {
background-color: rgb(27, 27, 27); background-color: rgb(27, 27, 27);
bottom: 0; bottom: 0;
right: var(--neg-party-width); right: var(--neg-party-width);
transition: all ease-in-out 0.3s;
width: var(--party-width); width: var(--party-width);
height: 100%; height: 100%;
color: white; color: white;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
z-index: 9999; z-index: 9999;
transition: all ease-in-out 0.3s !important;
} }
#party-routes-wrapper.visible { #party-routes-wrapper.visible {
right: 0; right: 0;
@@ -216,11 +302,11 @@ body.amazon .fyysciv.f1lobqlo.fk0j80b {
.watch-party-fab { .watch-party-fab {
z-index: 9999; z-index: 9999;
position: fixed !important; position: fixed !important;
bottom: 1rem !important; top: calc(100% - 75px) !important;
right: -2rem !important; right: -2rem !important;
border: 1px solid white; border: 1px solid white;
background-color: black !important; 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-image: url('chrome-extension://__MSG_@@extension_id__/android-icon-192x192.png');
background-position: center; background-position: center;
background-repeat: no-repeat; background-repeat: no-repeat;
@@ -243,10 +329,13 @@ body.amazon .fyysciv.f1lobqlo.fk0j80b {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
height: 100%; height: 100%;
max-height: 100% !important;
overflow-y: auto !important;
background: #424242; background: #424242;
padding: 0; padding: 0;
} }
.party-create-container .brand-title { .party-create-container .brand-title {
font-size: 18px !important;
padding: 36px; padding: 36px;
letter-spacing: 3.5px !important; letter-spacing: 3.5px !important;
} }
@@ -260,6 +349,7 @@ body.amazon .fyysciv.f1lobqlo.fk0j80b {
display: flex; display: flex;
justify-content: center; justify-content: center;
background: #424242; background: #424242;
box-shadow: none !important;
} }
.create-input-card * { .create-input-card * {
font-size: 12px !important; font-size: 12px !important;
@@ -271,9 +361,6 @@ body.amazon .fyysciv.f1lobqlo.fk0j80b {
.party-create-container .create-input-card .create-pin { .party-create-container .create-input-card .create-pin {
margin-bottom: 1rem; margin-bottom: 1rem;
} }
.party-create-container .create-input-card {
background-color: #2d2d2d;
}
.create-bottom-card { .create-bottom-card {
background-color: #202020; background-color: #202020;
padding: 30px 15px; padding: 30px 15px;
@@ -288,12 +375,12 @@ body.amazon .fyysciv.f1lobqlo.fk0j80b {
.create-bottom-card a:hover { .create-bottom-card a:hover {
color: #c0f300; color: #c0f300;
} }
.thread-container { .thread-wrapper {
max-height: 100%; max-height: 100%;
flex: 1; flex: 1;
overflow-y: auto; overflow-y: auto;
margin-bottom: 1rem; margin-bottom: 1rem;
padding: 0 15px; padding: 0;
margin-top: 1rem; margin-top: 1rem;
} }
.room-title { .room-title {
@@ -303,17 +390,6 @@ body.amazon .fyysciv.f1lobqlo.fk0j80b {
padding-right: 1rem !important; padding-right: 1rem !important;
font-size: 18px !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 { textarea::-webkit-scrollbar {
display: none; display: none;
} }
@@ -430,40 +506,6 @@ textarea::-webkit-scrollbar {
color: inherit !important; color: inherit !important;
font-size: 10px !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 { .message-send svg {
transform: scale(0.55); transform: scale(0.55);
transition: all ease 0.5s !important; transition: all ease 0.5s !important;
@@ -478,6 +520,7 @@ textarea::-webkit-scrollbar {
display: flex; display: flex;
align-items: center; align-items: center;
margin: 0 15px 15px 15px; margin: 0 15px 15px 15px;
margin-bottom: 0px;
border-radius: 26px; border-radius: 26px;
} }
.text-phone-panel .icon-button { .text-phone-panel .icon-button {
@@ -487,18 +530,24 @@ textarea::-webkit-scrollbar {
} }
.text-phone-panel .gif-button.gif-close { .text-phone-panel .gif-button.gif-close {
padding: 12px 0 !important; padding: 12px 0 !important;
max-height: 100%;
margin-left: -5px;
} }
.text-phone-panel .gif-button { .text-phone-panel .gif-button {
border-radius: 0 !important; border-radius: 0 !important;
height: 1.75em !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 { .text-phone-panel .gif-button svg {
width: 1.75em !important; width: 32px !important;
height: 1.75em !important; height: 32px !important;
} }
.text-phone-panel .gif-button.gif-close svg { .text-phone-panel .gif-button.gif-close svg {
width: 2em !important; height: 25px !important;
height: 1.5em !important; width: 25px !important;
} }
.MuiTooltip-popper { .MuiTooltip-popper {
@@ -533,6 +582,18 @@ body.amazon-prime .notification-switch .MuiSwitch-thumb {
.alerts-container { .alerts-container {
margin: 5px 0; 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 { .alert-card-content {
@@ -548,6 +609,10 @@ body.amazon-prime .notification-switch .MuiSwitch-thumb {
align-items: center; align-items: center;
font-size: 11px; font-size: 11px;
text-transform: uppercase; text-transform: uppercase;
flex: 1;
text-align: center !important;
justify-content: center;
max-width: 90% !important;
} }
#mic-permission-overlay { #mic-permission-overlay {
@@ -586,5 +651,262 @@ body.amazon-prime .notification-switch .MuiSwitch-thumb {
font-size: 14px !important; font-size: 14px !important;
letter-spacing: 1.75px !important; letter-spacing: 1.75px !important;
font-weight: 300 !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; 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 Written by Ayushya Amitabh <admin@chromewatchparty.com>, 2020
https://www.chromewatchparty.com/copyright 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 CITED ARE THE SOLE INTELECTUAL PROPERTY OF AYUSHYA AMITABH, AND MAY NOT BE
COPIED, EDITED, OR REPRODUCED WITHOUT PERMISSION. 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 React from 'react';
import ReactDOM from 'react-dom'; import ReactDOM from 'react-dom';
@@ -86,7 +79,7 @@ switch (getPlayerSource()) {
break; break;
case PLAYER_SOURCE.HBONOW: case PLAYER_SOURCE.HBONOW:
case PLAYER_SOURCE.HBOMAX: 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'); const hboClickInjectScript = document.createElement('script');
hboClickInjectScript.text = hboClickInject; hboClickInjectScript.text = hboClickInject;
document.head.appendChild(hboClickInjectScript); document.head.appendChild(hboClickInjectScript);
@@ -95,6 +88,12 @@ switch (getPlayerSource()) {
case PLAYER_SOURCE.PEACOCK: case PLAYER_SOURCE.PEACOCK:
document.body.classList.add('peacock'); document.body.classList.add('peacock');
break; 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: case PLAYER_SOURCE.LOCAL:
document.body.classList.add('local'); document.body.classList.add('local');
break; 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 FunctionModules = require('./modules/index');
const FunctionModules2 = require('./modules2/index'); const FunctionModules2 = require('./modules2/index');
const Webhooks = require('./webhooks/index');
const express = require('express'); const express = require('express');
const cors = require('cors'); 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 // VERSION 1
const app = express(); const app = express();
app.use(cors({ origin: true })); app.use(cors({ origin: true }));
app.use(FunctionModules.authenticator); app.use(FunctionModules.authenticator);
app.get('/validator', FunctionModules.tokenValidator); app.get('/validator/:partyName', FunctionModules.tokenValidator);
app.get('/refresher/:token', FunctionModules.tokenRefresher); app.get('/refresher/:token', FunctionModules.tokenRefresher);
app.post('/create', FunctionModules.createRoom); app.post('/create', FunctionModules.createRoom);
app.post('/join', FunctionModules.joinRoom); app.post('/join', FunctionModules.joinRoom);
@@ -16,6 +24,7 @@ app.post('/updatePlayer', FunctionModules.updatePlayer);
app.post('/updateVoxeet', FunctionModules.updateVoxeet); app.post('/updateVoxeet', FunctionModules.updateVoxeet);
app.delete('/leave/:rid/users/:uid', FunctionModules.leaveRoom); app.delete('/leave/:rid/users/:uid', FunctionModules.leaveRoom);
app.delete('/delete/:rid', FunctionModules.deleteRoom); app.delete('/delete/:rid', FunctionModules.deleteRoom);
app.put('/updateAdminOnly/:rid/:adminOnly', FunctionModules.updateAdminOnly);
// VERSION 2 // VERSION 2
const app2 = express(); const app2 = express();
@@ -34,4 +43,5 @@ app2.delete('/delete/:rid', FunctionModules2.deleteRoom);
module.exports = { module.exports = {
v1: app, v1: app,
v2: app2, v2: app2,
webhooks,
}; };
+2 -5
View File
@@ -1,4 +1,3 @@
const FunctionModules = require('./modules/index');
const functions = require('firebase-functions'); const functions = require('firebase-functions');
const secret = require('./FirebaseSecret.json'); const secret = require('./FirebaseSecret.json');
const admin = require('firebase-admin'); const admin = require('firebase-admin');
@@ -15,7 +14,5 @@ admin.initializeApp(FIREBASE_OPTIONS);
exports.widgets = functions.https.onRequest(REST.v1); exports.widgets = functions.https.onRequest(REST.v1);
// VERSION 2 // VERSION 2
exports.widgets2 = functions.https.onRequest(REST.v2); exports.widgets2 = functions.https.onRequest(REST.v2);
// WEBHOOKS
exports.scheduledCleanUp = functions.pubsub exports.webhooks = functions.https.onRequest(REST.webhooks);
.schedule('every 60 minutes')
.onRun(FunctionModules.scheduledCleanUp);
+1 -1
View File
@@ -22,7 +22,7 @@ module.exports = (req, res, next) => {
bodydata.uid = user.uid; bodydata.uid = user.uid;
req.body = JSON.stringify(bodydata); 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 }); req.body = JSON.stringify({ uid: user.uid });
} }
return next(); 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(), createdAt: new Date(),
updatedAt: new Date(), updatedAt: new Date(),
adminUid: bodydata.uid, adminUid: bodydata.uid,
adminOnly: bodydata.adminOnly,
}) })
.catch((err) => { .catch((err) => {
res.status(500).send({ message: 'Unable to start party.' }); res.status(500).send({ message: 'Unable to start party.' });
@@ -46,23 +47,6 @@ module.exports = async (req, res) => {
err 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 await admin
.firestore() .firestore()
.collection('rooms') .collection('rooms')
-1
View File
@@ -2,7 +2,6 @@ const admin = require('firebase-admin');
module.exports = async (req, res) => { module.exports = async (req, res) => {
console.log('Requested Delete - Room - ', req.params.rid); console.log('Requested Delete - Room - ', req.params.rid);
console.log(req.body);
const isAdmin = await admin const isAdmin = await admin
.firestore() .firestore()
.collection('rooms') .collection('rooms')
+2 -4
View File
@@ -1,6 +1,4 @@
const authenticator = require('./authenticator'); const authenticator = require('./authenticator');
const cleanUpRoom = require('./cleanUpRoom');
const scheduledCleanUp = require('./scheduledCleanUp');
const createRoom = require('./createRoom'); const createRoom = require('./createRoom');
const joinRoom = require('./joinRoom'); const joinRoom = require('./joinRoom');
const sendMessage = require('./sendMessage'); const sendMessage = require('./sendMessage');
@@ -11,6 +9,7 @@ const updatePlayer = require('./updatePlayer');
const tokenValidator = require('./tokenValidator'); const tokenValidator = require('./tokenValidator');
const tokenRefresher = require('./tokenRefresher'); const tokenRefresher = require('./tokenRefresher');
const updateVoxeet = require('./updateVoxeet'); const updateVoxeet = require('./updateVoxeet');
const updateAdminOnly = require('./updateAdminOnly');
module.exports = { module.exports = {
authenticator, authenticator,
@@ -20,10 +19,9 @@ module.exports = {
updatePlayer, updatePlayer,
leaveRoom, leaveRoom,
deleteRoom, deleteRoom,
scheduledCleanUp,
cleanUpRoom,
timeDiff, timeDiff,
tokenValidator, tokenValidator,
tokenRefresher, tokenRefresher,
updateVoxeet, updateVoxeet,
updateAdminOnly,
}; };
+1 -2
View File
@@ -1,5 +1,4 @@
const admin = require('firebase-admin'); const admin = require('firebase-admin');
const { firestore } = require('firebase-admin');
module.exports = async (req, res) => { module.exports = async (req, res) => {
const bodydata = JSON.parse(req.body); const bodydata = JSON.parse(req.body);
@@ -137,7 +136,7 @@ module.exports = async (req, res) => {
.update({ .update({
player: { player: {
showName: null, showName: null,
source: null, source: bodydata.source,
status: 'NOT_PLAYING', status: 'NOT_PLAYING',
time: 0, time: 0,
updatedBy: bodydata.uid, updatedBy: bodydata.uid,
+2 -1
View File
@@ -1,7 +1,7 @@
const admin = require('firebase-admin'); const admin = require('firebase-admin');
module.exports = async (req, res) => { 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 const roomExists = await admin
.firestore() .firestore()
@@ -21,6 +21,7 @@ module.exports = async (req, res) => {
time: new Date(), time: new Date(),
uid, uid,
type: type || '', type: type || '',
replyTo: replyTo || null,
}) })
.catch((err) => { .catch((err) => {
console.error(`Failed to send message to party : \n\t Room: ${roomId} \n\t ${uid}`, 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'); const VoxeetSecret = require('../VoxeetSecret.json');
module.exports = async (req, res) => { module.exports = async (req, res) => {
res.status(403).send({ message: 'Voice calls are unavailable temporarily.' }); const { key, secret } = VoxeetSecret;
// const { key, secret } = VoxeetSecret; const credentials = btoa(encodeURI(key) + ':' + encodeURI(secret));
// const credentials = btoa(encodeURI(key) + ':' + encodeURI(secret)); const headers = {
// const headers = { 'Content-Type': 'application/json',
// 'Content-Type': 'application/json', 'Cache-Control': 'no-cache',
// 'Cache-Control': 'no-cache', Authorization: 'Basic ' + credentials,
// Authorization: 'Basic ' + credentials, };
// }; const oauth = await fetch(VoxeetSecret.refreshURL, {
// const oauth = await fetch(VoxeetSecret.refreshURL, { method: 'POST',
// method: 'POST', headers,
// headers, body: JSON.stringify({
// body: JSON.stringify({ refresh_token: req.params.token,
// refresh_token: req.params.token, }),
// }), })
// }) .then((oauthResponse) => oauthResponse.json())
// .then((oauthResponse) => oauthResponse.json()) .catch((err) => res.status(500).send(err));
// .catch((err) => res.status(500).send(err)); if (oauth) res.status(200).send(oauth);
// if (oauth) res.status(200).send(oauth);
}; };
+60 -18
View File
@@ -1,22 +1,64 @@
const VoxeetSecret = require('../VoxeetSecret.json'); const VoxeetSecret = require('../VoxeetSecret.json');
const admin = require('firebase-admin');
const fetch = require('node-fetch'); const fetch = require('node-fetch');
module.exports = async (req, res) => { module.exports = async (req, res) => {
res.status(403).send({ message: 'Voice calls are unavailable temporarily.' }); const bodyData = JSON.parse(req.body);
// const { key, secret } = VoxeetSecret; const { key, secret } = VoxeetSecret;
// const credentials = Buffer.from(encodeURI(key) + ':' + encodeURI(secret)).toString('base64'); const credentials = Buffer.from(encodeURI(key) + ':' + encodeURI(secret)).toString('base64');
// const headers = { const headers = {
// 'Content-Type': 'application/json', 'Content-Type': 'application/json',
// 'Cache-Control': 'no-cache', 'Cache-Control': 'no-cache',
// Authorization: 'Basic ' + credentials, Authorization: 'Basic ' + credentials,
// }; };
// const oauth = await fetch(VoxeetSecret.validateURL, { const oauth = await fetch(VoxeetSecret.validateURL, {
// method: 'POST', method: 'POST',
// headers, headers,
// body: JSON.stringify({ body: JSON.stringify({
// grant_type: 'client_credentials', grant_type: 'client_credentials',
// }), }),
// }) }).then((oauthResponse) => oauthResponse.json())
// .then((oauthResponse) => oauthResponse.json()) .catch((err) => res.status(500).send({err, message: 'Unable to generate token for call'}));
// .catch((err) => res.status(500).send(err));
// if (oauth) res.status(200).send(oauth); 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) => { module.exports = async (req, res) => {
const { player, roomId, uid, showChanged, userName } = JSON.parse(req.body); const { player, roomId, uid, showChanged, userName } = JSON.parse(req.body);
// const statusString = if (!roomId) return res.status(204).send({ message: 'Party name not sent.' });
// 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);
const [roomExists, roomData] = await admin const [roomExists, roomData] = await admin
.firestore() .firestore()
.collection('rooms') .collection('rooms')
@@ -30,18 +19,20 @@ module.exports = async (req, res) => {
.get() .get()
.then((userSnap) => userSnap.exists); .then((userSnap) => userSnap.exists);
if (userExists) { if (userExists) {
if (showChanged === true) { if (showChanged === true && player.showName) {
await admin if (roomData.player.showName !== player.showName) {
.firestore() await admin
.collection('rooms') .firestore()
.doc(roomId) .collection('rooms')
.collection('messages') .doc(roomId)
.add({ .collection('messages')
uid: '<system>', .add({
name: '', uid: '<system>',
text: `${userName} changed \n${player.showName ? 'to ' + player.showName : ''}`, name: '',
time: new Date(), text: `${userName} changed \n${player.showName ? 'to ' + player.showName : ''}`,
}); time: new Date(),
});
}
} }
if (roomData.status !== player.status) { if (roomData.status !== player.status) {
await admin 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); 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 }); 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' }); return res.status(200).send({ message: 'Synced party player' });
} else { } else {
return res.status(208).send({ message: 'Player already up to date' }); 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) => { 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 // GET ROOM PIN
const roomPin = await admin const [exists, roomPin, conferencePending, partyConference] = await admin
.firestore() .firestore()
.collection('rooms') .collection("rooms")
.doc(partyName) .doc(partyName)
.get() .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 // CHECK IF ROOM EXISTS
if (roomPin) { if (exists === true) {
// CHECK FOR CORRECT PIN if (roomPin) {
if (roomPin === partyPin) { // CHECK FOR CORRECT PIN
// UPDATE CONFERENCE DETAILS if (roomPin === partyPin) {
await admin if (conferencePending) {
.firestore() if (conferencePending === uid) {
.collection('rooms') // UPDATE CONFERENCE DETAILS
.doc(partyName) await admin
.update({ conference }) .firestore()
.catch((err) => .collection("rooms")
res.status(500).send({ message: 'Unable to update the call details', error: err }) .doc(partyName)
); .update({ conference })
// ADD SYSTEM MESSAGE .catch((err) =>
await admin res.status(500).send({
.firestore() message: "Unable to update the call details",
.collection('rooms') error: err,
.doc(partyName) })
.collection('messages') );
.add({ // REMOVE PENDING STATUS
uid: '<system>', await admin
name: '', .firestore()
text: `${userName} started the call.`, .collection("rooms")
time: new Date(), .doc(partyName)
}) .update({
.catch((err) => console.error('Failed to add update conference message', err)); conferencePending: admin.firestore.FieldValue.delete(),
res.status(200).send({ message: 'Started the conference' }); })
} else { .catch((err) =>
// WRONG ROOM PIN console.error("Failed to remove pending status", err)
return res.status(401).send({ message: 'Wrong party pin, try rejoining' }); );
// 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 { } else {
// ROOM DOESN'T EXIST // 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) => { module.exports = async (req, res) => {
const bodydata = JSON.parse(req.body); const bodydata = JSON.parse(req.body);
console.log('Requested Create - UID - ', bodydata.uid); console.log("Requested Create - UID - ", bodydata.uid);
if (!bodydata.userName) res.status(400).send({ message: 'Missing user name' }); if (!bodydata.userName)
if (!bodydata.partyName) res.status(400).send({ message: 'Missing party name' }); res.status(400).send({ message: "Missing user name" });
if (!bodydata.partyPin) res.status(400).send({ message: 'Missing party pin' }); 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 const roomNameTaken = await admin
.firestore() .firestore()
.collection('rooms') .collection("rooms")
.doc(bodydata.partyName.replace('/', '\\')) .doc(bodydata.partyName.replace("/", "\\"))
.get() .get()
.then((docSnap) => docSnap.exists); .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 await admin
.firestore() .firestore()
.collection('rooms') .collection("rooms")
.doc(bodydata.partyName) .doc(bodydata.partyName)
.set({ .set({
name: bodydata.partyName, name: bodydata.partyName,
@@ -26,17 +32,17 @@ module.exports = async (req, res) => {
adminUid: bodydata.uid, adminUid: bodydata.uid,
}) })
.catch((err) => { .catch((err) => {
res.status(500).send({ message: 'Unable to start party.' }); res.status(500).send({ message: "Unable to start party." });
console.error(err); console.error(err);
}); });
await admin await admin
.firestore() .firestore()
.collection('rooms') .collection("rooms")
.doc(bodydata.partyName) .doc(bodydata.partyName)
.collection('messages') .collection("messages")
.add({ .add({
uid: '<system>', uid: "<system>",
name: '', name: "",
text: `${bodydata.userName} started the party.`, text: `${bodydata.userName} started the party.`,
time: new Date(), time: new Date(),
}) })
@@ -48,9 +54,9 @@ module.exports = async (req, res) => {
}); });
await admin await admin
.firestore() .firestore()
.collection('rooms') .collection("rooms")
.doc(bodydata.partyName) .doc(bodydata.partyName)
.collection('users') .collection("users")
.doc(bodydata.uid) .doc(bodydata.uid)
.set({ .set({
admin: true, admin: true,
@@ -62,5 +68,34 @@ module.exports = async (req, res) => {
err 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({ .update({
player: { player: {
showName: null, showName: null,
source: roomSource, source: bodydata.source,
status: 'NOT_PLAYING', status: 'NOT_PLAYING',
time: 0, time: 0,
updatedBy: bodydata.uid, updatedBy: bodydata.uid,
+13 -11
View File
@@ -32,17 +32,19 @@ module.exports = async (req, res) => {
.then((userSnap) => userSnap.exists); .then((userSnap) => userSnap.exists);
if (userExists) { if (userExists) {
if (showChanged === true && player.showName) { if (showChanged === true && player.showName) {
await admin if (player.showName !== roomData.player.showName) {
.firestore() await admin
.collection('rooms') .firestore()
.doc(roomId) .collection('rooms')
.collection('messages') .doc(roomId)
.add({ .collection('messages')
uid: '<system>', .add({
name: '', uid: '<system>',
text: `${userName} changed \n${player.showName ? 'to ' + player.showName : ''}`, name: '',
time: new Date(), text: `${userName} changed \n${player.showName ? 'to ' + player.showName : ''}`,
}); time: new Date(),
});
}
} }
if (roomData.status !== player.status) { if (roomData.status !== player.status) {
await admin await admin
+4 -4
View File
@@ -1,6 +1,6 @@
{ {
"name": "watch-party-api", "name": "watch-party-api",
"version": "1.2.1", "version": "1.6.0",
"lockfileVersion": 1, "lockfileVersion": 1,
"requires": true, "requires": true,
"dependencies": { "dependencies": {
@@ -1909,9 +1909,9 @@
} }
}, },
"lodash": { "lodash": {
"version": "4.17.15", "version": "4.17.19",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz",
"integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==" "integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ=="
}, },
"lodash.at": { "lodash.at": {
"version": "4.6.0", "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 admin = require("firebase-admin");
const cleanUpRoom = require('./cleanUpRoom'); const cleanUpRoom = require("./cleanUpRoom");
const timeDiff = (ds1, ds2) => { const timeDiff = (ds1, ds2) => {
const d1 = new Date(ds1); const d1 = new Date(ds1);
@@ -9,11 +9,12 @@ const timeDiff = (ds1, ds2) => {
return Math.abs(Math.round(diff)); return Math.abs(Math.round(diff));
}; };
module.exports = async () => { module.exports = async (req, res) => {
const startedTime = new Date(); const startedTime = new Date();
res.status(200).send({ message: "Started processing" });
const roomList = await admin const roomList = await admin
.firestore() .firestore()
.collection('rooms') .collection("rooms")
.listDocuments() .listDocuments()
.then((rL) => rL) .then((rL) => rL)
.catch((err) => { .catch((err) => {
@@ -29,11 +30,11 @@ module.exports = async () => {
if ( if (
// MORE THAN 24 HOURS AND NOT IN USE // MORE THAN 24 HOURS AND NOT IN USE
(timeDiff(roomSnap.createTime.toDate(), startedTime) >= 24 && (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 // LESS THAN 24 HOURS BUT NOT IN USE
timeDiff(roomSnap.updateTime.toDate(), startedTime) >= 12 timeDiff(roomSnap.updateTime.toDate(), startedTime) >= 12
) { ) {
await cleanUpRoom(roomSnap.id); cleanUpRoom(roomSnap.id);
console.info(`Deleted room ${roomSnap.id} from auto cleanup`); console.info(`Deleted room ${roomSnap.id} from auto cleanup`);
} }
return; 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 Unauthorized copying of this file, via any medium is strictly prohibited
Proprietary and confidential Proprietary and confidential
Written by Ayushya Amitabh <admin@chromewatchparty.com>, 2020 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> <html>
<head> <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 Unauthorized copying of this file, via any medium is strictly prohibited
Proprietary and confidential Proprietary and confidential
Written by Ayushya Amitabh <admin@chromewatchparty.com>, 2020 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> <html>
<head> <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 Unauthorized copying of this file, via any medium is strictly prohibited
Proprietary and confidential Proprietary and confidential
Written by Ayushya Amitabh <admin@chromewatchparty.com>, 2020 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> <html>
<head> <head>
<meta http-equiv="refresh" content="0; URL='https://request.chromewatchparty.com'" /> <meta http-equiv="refresh" content="0; URL='https://request.chromewatchparty.com'" />
<title>Watch Party | Beta</title> <title>Watch Party | Beta</title>
</head> </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> <body>
Redirecting... Redirecting...
</body> </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 Unauthorized copying of this file, via any medium is strictly prohibited
Proprietary and confidential Proprietary and confidential
Written by Ayushya Amitabh <admin@chromewatchparty.com>, 2020 Written by Ayushya Amitabh <admin@chromewatchparty.com>, 2020
https://www.chromewatchparty.com/copyright
========================================================================= =========================================================================
ALL CONTENT ON THIS WEBSITE, UNLESS OTHERWISE CITED ARE THE SOLE ALL CONTENT ON WATCH PARTY'S WEBSITE OR IN WATCH PARTY'S CHROME EXTENSION APP, UNLESS OTHERWISE
INTELECTUAL PROPERTY OF AYUSHYA AMITABH, AND MAY NOT BE COPIED, EDITED, CITED ARE THE SOLE INTELECTUAL PROPERTY OF AYUSHYA AMITABH, AND MAY NOT BE
OR REPRODUCED WITHOUT PERMISSION. 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.
-------------------------------------------------------------------------> ------------------------------------------------------------------------->
<html> <html>
<head> <head>
<link rel="apple-touch-icon" sizes="57x57" href="../icons/apple-icon-57x57.png" /> <link
<link rel="apple-touch-icon" sizes="60x60" href="../icons/apple-icon-60x60.png" /> rel="apple-touch-icon"
<link rel="apple-touch-icon" sizes="72x72" href="../icons/apple-icon-72x72.png" /> sizes="57x57"
<link rel="apple-touch-icon" sizes="76x76" href="../icons/apple-icon-76x76.png" /> href="../icons/apple-icon-57x57.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
<link rel="apple-touch-icon" sizes="144x144" href="../icons/apple-icon-144x144.png" /> rel="apple-touch-icon"
<link rel="apple-touch-icon" sizes="152x152" href="../icons/apple-icon-152x152.png" /> sizes="60x60"
<link rel="apple-touch-icon" sizes="180x180" href="../icons/apple-icon-180x180.png" /> href="../icons/apple-icon-60x60.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
<link rel="icon" type="image/png" sizes="96x96" href="../icons/favicon-96x96.png" /> rel="apple-touch-icon"
<link rel="icon" type="image/png" sizes="16x16" href="../icons/favicon-16x16.png" /> 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="icon" href="../icons/favicon.ico" />
<link rel="manifest" href="../manifest.json" /> <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="../font.css" />
<link rel="stylesheet" type="text/css" href="../styles/index.css" /> <link rel="stylesheet" type="text/css" href="../styles/index.css" />
<script src="../scripts/Stickied.js"></script> <script src="../scripts/Stickied.js"></script>
<title>Watch Party | Changelog</title> <title>Watch Party | Changelog</title>
</head> </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"> <body class="copyright">
<div class="landing"> <div class="landing">
<div class="logo-circle"></div> <div class="logo-circle"></div>
<div class="logo-circle"></div> <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"> <h5 data-font="sans" class="logo-tag">
WATCH YOUR FAVORITES,<br />WITH YOUR FAVORITE PEOPLE WATCH YOUR FAVORITES,<br />WITH YOUR FAVORITE PEOPLE
</h5> </h5>
@@ -61,26 +132,37 @@ Peacock TV logo is a trademark and intellectual property of Peacock TV & NBC Uni
<div <div
class="supported-logo" class="supported-logo"
style="background-image: url(../media/versions/160.png);" style="background-image: url(../media/versions/160.png);"
data-upcoming-support="Work In Progress" data-upcoming-support="See Changes"
></div> ></div>
</a> </a>
</div> </div>
</div> </div>
<footer data-font="sans"> <footer data-font="sans">
<p class="title built-with"> <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>
<p class="title built-with"> <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://firebase.google.com/">FIREBASE</a>,
<a href="https://material-ui.com/">MATERIAL-UI</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://reactjs.org/">REACT JS</a>,
<a href="https://giphy.com/">GIPHY</a>, & <a href="https://dolby.io/">DOLBY VOXEET</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> </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 Support Watch Party
</a> </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 >Get Chrome Extension</a
> >
<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 Unauthorized copying of this file, via any medium is strictly prohibited
Proprietary and confidential Proprietary and confidential
Written by Ayushya Amitabh <admin@chromewatchparty.com>, 2020 Written by Ayushya Amitabh <admin@chromewatchparty.com>, 2020
https://www.chromewatchparty.com/copyright
========================================================================= =========================================================================
ALL CONTENT ON THIS WEBSITE, UNLESS OTHERWISE CITED ARE THE SOLE ALL CONTENT ON WATCH PARTY'S WEBSITE OR IN WATCH PARTY'S CHROME EXTENSION APP, UNLESS OTHERWISE
INTELECTUAL PROPERTY OF AYUSHYA AMITABH, AND MAY NOT BE COPIED, EDITED, CITED ARE THE SOLE INTELECTUAL PROPERTY OF AYUSHYA AMITABH, AND MAY NOT BE
OR REPRODUCED WITHOUT PERMISSION. 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.
-------------------------------------------------------------------------> ------------------------------------------------------------------------->
<html> <html>
<head> <head>
<link rel="apple-touch-icon" sizes="57x57" href="../../icons/apple-icon-57x57.png" /> <link
<link rel="apple-touch-icon" sizes="60x60" href="../../icons/apple-icon-60x60.png" /> rel="apple-touch-icon"
<link rel="apple-touch-icon" sizes="72x72" href="../../icons/apple-icon-72x72.png" /> sizes="57x57"
<link rel="apple-touch-icon" sizes="76x76" href="../../icons/apple-icon-76x76.png" /> href="../../icons/apple-icon-57x57.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
<link rel="apple-touch-icon" sizes="144x144" href="../../icons/apple-icon-144x144.png" /> rel="apple-touch-icon"
<link rel="apple-touch-icon" sizes="152x152" href="../../icons/apple-icon-152x152.png" /> sizes="60x60"
<link rel="apple-touch-icon" sizes="180x180" href="../../icons/apple-icon-180x180.png" /> href="../../icons/apple-icon-60x60.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
<link rel="icon" type="image/png" sizes="96x96" href="../../icons/favicon-96x96.png" /> rel="apple-touch-icon"
<link rel="icon" type="image/png" sizes="16x16" href="../../icons/favicon-16x16.png" /> 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="icon" href="../../icons/favicon.ico" />
<link rel="manifest" href="../../manifest.json" /> <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="../../font.css" />
<link rel="stylesheet" type="text/css" href="../../styles/index.css" /> <link rel="stylesheet" type="text/css" href="../../styles/index.css" />
<script src="../../scripts/Stickied.js"></script> <script src="../../scripts/Stickied.js"></script>
<title>Watch Party | V1.5.2</title> <title>Watch Party | V1.5.2</title>
</head> </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"> <body class="copyright welcome">
<div class="landing"> <div class="landing">
<div class="logo-circle"></div> <div class="logo-circle"></div>
<div class="logo-circle"></div> <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"> <h5 data-font="sans" class="logo-tag">
WATCH YOUR FAVORITES,<br />WITH YOUR FAVORITE PEOPLE WATCH YOUR FAVORITES,<br />WITH YOUR FAVORITE PEOPLE
</h5> </h5>
</div> </div>
<div class="features"> <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-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"> <p data-font="sans" class="feature-text">
Fixed Netflix bug to autodetect player. Fixed Netflix bug to autodetect player.
</p> </p>
</div> </div>
<div class="feature-card"> <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"> <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> </p>
</div> </div>
<div class="feature-card"> <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"> <p data-font="sans" class="feature-text">
Gif scrollbar is now easier to grab and scroll. Gif scrollbar is now easier to grab and scroll.
</p> </p>
</div> </div>
<div class="feature-card"> <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"> <p data-font="sans" class="feature-text">
Fullscreen support, use the button in the sidebar to have access to Watch Party in Fullscreen support, use the button in the sidebar to have access to
fullscreen Watch Party in fullscreen
</p> </p>
</div> </div>
<div class="feature-card"> <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"> <p data-font="sans" class="feature-text">
Added auto rejoin (as long you return within 1 minute) Added auto rejoin (as long you return within 1 minute)
</p> </p>
@@ -120,13 +229,14 @@ Peacock TV logo is a trademark and intellectual property of Peacock TV & NBC Uni
</div> </div>
<div class="tip"> <div class="tip">
<p data-font="sans-bold"> <p data-font="sans-bold">
> Netflix player update (fixes issue when player was not detected if video started before > Netflix player update (fixes issue when player was not detected if
party) video started before party)
</p> </p>
</div> </div>
<div class="tip"> <div class="tip">
<p data-font="sans-bold"> <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> </p>
</div> </div>
<div class="tip"> <div class="tip">
@@ -149,19 +259,30 @@ Peacock TV logo is a trademark and intellectual property of Peacock TV & NBC Uni
</div> </div>
<footer data-font="sans"> <footer data-font="sans">
<p class="title built-with"> <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>
<p class="title built-with"> <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://firebase.google.com/">FIREBASE</a>,
<a href="https://material-ui.com/">MATERIAL-UI</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://reactjs.org/">REACT JS</a>,
<a href="https://giphy.com/">GIPHY</a>, & <a href="https://dolby.io/">DOLBY VOXEET</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> </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 Support Watch Party
</a> </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 >Get Chrome Extension</a
> >
<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 Unauthorized copying of this file, via any medium is strictly prohibited
Proprietary and confidential Proprietary and confidential
Written by Ayushya Amitabh <admin@chromewatchparty.com>, 2020 Written by Ayushya Amitabh <admin@chromewatchparty.com>, 2020
https://www.chromewatchparty.com/copyright
========================================================================= =========================================================================
ALL CONTENT ON THIS WEBSITE, UNLESS OTHERWISE CITED ARE THE SOLE ALL CONTENT ON WATCH PARTY'S WEBSITE OR IN WATCH PARTY'S CHROME EXTENSION APP, UNLESS OTHERWISE
INTELECTUAL PROPERTY OF AYUSHYA AMITABH, AND MAY NOT BE COPIED, EDITED, CITED ARE THE SOLE INTELECTUAL PROPERTY OF AYUSHYA AMITABH, AND MAY NOT BE
OR REPRODUCED WITHOUT PERMISSION. 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.
-------------------------------------------------------------------------> ------------------------------------------------------------------------->
<html> <html>
<head> <head>
<link rel="apple-touch-icon" sizes="57x57" href="../../icons/apple-icon-57x57.png" /> <link
<link rel="apple-touch-icon" sizes="60x60" href="../../icons/apple-icon-60x60.png" /> rel="apple-touch-icon"
<link rel="apple-touch-icon" sizes="72x72" href="../../icons/apple-icon-72x72.png" /> sizes="57x57"
<link rel="apple-touch-icon" sizes="76x76" href="../../icons/apple-icon-76x76.png" /> href="../../icons/apple-icon-57x57.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
<link rel="apple-touch-icon" sizes="144x144" href="../../icons/apple-icon-144x144.png" /> rel="apple-touch-icon"
<link rel="apple-touch-icon" sizes="152x152" href="../../icons/apple-icon-152x152.png" /> sizes="60x60"
<link rel="apple-touch-icon" sizes="180x180" href="../../icons/apple-icon-180x180.png" /> href="../../icons/apple-icon-60x60.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
<link rel="icon" type="image/png" sizes="96x96" href="../../icons/favicon-96x96.png" /> rel="apple-touch-icon"
<link rel="icon" type="image/png" sizes="16x16" href="../../icons/favicon-16x16.png" /> 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="icon" href="../../icons/favicon.ico" />
<link rel="manifest" href="../../manifest.json" /> <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="../../font.css" />
<link rel="stylesheet" type="text/css" href="../../styles/index.css" /> <link rel="stylesheet" type="text/css" href="../../styles/index.css" />
<script src="../../scripts/Stickied.js"></script> <script src="../../scripts/Stickied.js"></script>
<title>Watch Party | V1.6.0</title> <title>Watch Party | V1.6.0</title>
</head> </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"> <body class="copyright welcome">
<div class="landing"> <div class="landing">
<div class="logo-circle"></div> <div class="logo-circle"></div>
<div class="logo-circle"></div> <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"> <h5 data-font="sans" class="logo-tag">
WATCH YOUR FAVORITES,<br />WITH YOUR FAVORITE PEOPLE WATCH YOUR FAVORITES,<br />WITH YOUR FAVORITE PEOPLE
</h5> </h5>
</div> </div>
<div class="features"> <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-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"> <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> </p>
</div> </div>
</div> </div>
<div id="tips" class="landing"> <div id="tips" class="landing">
<p data-font="sans-bold" class="title"> <p data-font="sans-bold" class="title">
PLANNED CHANGES' DETAILS LISTED CHANGE DETAILS
</p> </p>
<div class="tip"> <div class="tip">
<p data-font="sans-bold"> <p data-font="sans-bold">
Admin only control mode Add support for Crunchyroll
</p> </p>
</div> </div>
<div class="tip"> <div class="tip">
<p data-font="sans-bold"> <p data-font="sans-bold">
Interactive system messages Add support for Google Play Movies & TV
</p> </p>
</div> </div>
<div class="tip"> <div class="tip">
<p data-font="sans-bold"> <p data-font="sans-bold">
Reply to messages with quoting Completely rebuilt Chat UI
</p> </p>
</div> </div>
<div class="tip"> <div class="tip">
<p data-font="sans-bold"> <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> </p>
</div> </div>
<a <a
@@ -93,19 +295,30 @@ Peacock TV logo is a trademark and intellectual property of Peacock TV & NBC Uni
</div> </div>
<footer data-font="sans"> <footer data-font="sans">
<p class="title built-with"> <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>
<p class="title built-with"> <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://firebase.google.com/">FIREBASE</a>,
<a href="https://material-ui.com/">MATERIAL-UI</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://reactjs.org/">REACT JS</a>,
<a href="https://giphy.com/">GIPHY</a>, & <a href="https://dolby.io/">DOLBY VOXEET</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> </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 Support Watch Party
</a> </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 >Get Chrome Extension</a
> >
<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 Unauthorized copying of this file, via any medium is strictly prohibited
Proprietary and confidential Proprietary and confidential
Written by Ayushya Amitabh <admin@chromewatchparty.com>, 2020 Written by Ayushya Amitabh <admin@chromewatchparty.com>, 2020
https://www.chromewatchparty.com/copyright
========================================================================= =========================================================================
ALL CONTENT ON THIS WEBSITE, UNLESS OTHERWISE CITED ARE THE SOLE ALL CONTENT ON WATCH PARTY'S WEBSITE OR IN WATCH PARTY'S CHROME EXTENSION APP, UNLESS OTHERWISE
INTELECTUAL PROPERTY OF AYUSHYA AMITABH, AND MAY NOT BE COPIED, EDITED, CITED ARE THE SOLE INTELECTUAL PROPERTY OF AYUSHYA AMITABH, AND MAY NOT BE
OR REPRODUCED WITHOUT PERMISSION. 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.
-------------------------------------------------------------------------> ------------------------------------------------------------------------->
<html> <html>
<head> <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> <script src="../scripts/Stickied.js"></script>
<title>Watch Party | Copyright</title> <title>Watch Party | Copyright</title>
</head> </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"> <body class="copyright">
<div class="landing"> <div class="landing">
<div class="logo-circle"></div> <div class="logo-circle"></div>
<div class="logo-circle"></div> <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> <h1 data-font="serif" class="logo"><a href="../">WATCH PARTY</a> | COPYRIGHT</h1>
<h5 data-font="sans" class="logo-tag"> <h5 data-font="sans" class="logo-tag">
Copyright &COPY; 2020 Ayushya Amitabh. All Rights Reserved. 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 /> 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, HBO Now and HBO Max logos are trademark and intellectual property of WarnerMedia Direct,
LLC.<br /> 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 />
<br /> <br />
</p> </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 Unauthorized copying of this file, via any medium is strictly prohibited
Proprietary and confidential Proprietary and confidential
Written by Ayushya Amitabh <admin@chromewatchparty.com>, 2020 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 { @font-face {
@@ -35,10 +40,13 @@ Written by Ayushya Amitabh <admin@chromewatchparty.com>, 2020
} }
[data-font='serif'] { [data-font='serif'] {
font-family: 'PixelSplitter', sans-serif; font-family: 'PixelSplitter', sans-serif;
font-display: swap;
} }
[data-font='sans'] { [data-font='sans'] {
font-family: 'Glacial', sans-serif; font-family: 'Glacial', sans-serif;
font-display: swap;
} }
[data-font='sans-bold'] { [data-font='sans-bold'] {
font-family: 'GlacialBold', sans-serif; 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 Unauthorized copying of this file, via any medium is strictly prohibited
Proprietary and confidential Proprietary and confidential
Written by Ayushya Amitabh <admin@chromewatchparty.com>, 2020 Written by Ayushya Amitabh <admin@chromewatchparty.com>, 2020
https://www.chromewatchparty.com/copyright
========================================================================= =========================================================================
ALL CONTENT ON THIS WEBSITE, UNLESS OTHERWISE CITED ARE THE SOLE ALL CONTENT ON WATCH PARTY'S WEBSITE OR IN WATCH PARTY'S CHROME EXTENSION APP, UNLESS OTHERWISE
INTELECTUAL PROPERTY OF AYUSHYA AMITABH, AND MAY NOT BE COPIED, EDITED, CITED ARE THE SOLE INTELECTUAL PROPERTY OF AYUSHYA AMITABH, AND MAY NOT BE
OR REPRODUCED WITHOUT PERMISSION. 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.
-------------------------------------------------------------------------> ------------------------------------------------------------------------->
<html> <html>
<head> <head>
<link rel="apple-touch-icon" sizes="57x57" href="./icons/apple-icon-57x57.png" /> <link
<link rel="apple-touch-icon" sizes="60x60" href="./icons/apple-icon-60x60.png" /> rel="apple-touch-icon"
<link rel="apple-touch-icon" sizes="72x72" href="./icons/apple-icon-72x72.png" /> sizes="57x57"
<link rel="apple-touch-icon" sizes="76x76" href="./icons/apple-icon-76x76.png" /> href="./icons/apple-icon-57x57.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
<link rel="apple-touch-icon" sizes="144x144" href="./icons/apple-icon-144x144.png" /> rel="apple-touch-icon"
<link rel="apple-touch-icon" sizes="152x152" href="./icons/apple-icon-152x152.png" /> sizes="60x60"
<link rel="apple-touch-icon" sizes="180x180" href="./icons/apple-icon-180x180.png" /> href="./icons/apple-icon-60x60.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
<link rel="icon" type="image/png" sizes="96x96" href="./icons/favicon-96x96.png" /> rel="apple-touch-icon"
<link rel="icon" type="image/png" sizes="16x16" href="./icons/favicon-16x16.png" /> 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="icon" href="./icons/favicon.ico" />
<link rel="manifest" href="./manifest.json" /> <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="./font.css" />
<link rel="stylesheet" type="text/css" href="./styles/index.css" /> <link rel="stylesheet" type="text/css" href="./styles/index.css" />
<script src="./scripts/Stickied.js"></script> <script src="./scripts/Stickied.js"></script>
<title>Watch Party</title> <title>Watch Party</title>
</head> </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="landing">
<div class="logo-circle"></div> <div class="logo-circle"></div>
<div class="logo-circle"></div> <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> <h1 data-font="serif" class="logo">WATCH PARTY</h1>
<h5 data-font="sans" class="logo-tag"> <h5 data-font="sans" class="logo-tag">
WATCH YOUR FAVORITES,<br />WITH YOUR FAVORITE PEOPLE WATCH YOUR FAVORITES,<br />WITH YOUR FAVORITE PEOPLE
</h5> </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 class="supported">
<div data-full-support class="supported-logo" id="netflix"></div> <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="hulu"></div>
<div data-full-support class="supported-logo" id="disney"></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="prime"></div>
<div data-full-support class="supported-logo" id="youtube"></div> <div data-full-support class="supported-logo" id="youtube"></div>
<div data-beta-support class="supported-logo" id="hbonow"></div> <div data-full-support class="supported-logo" id="hbonow"></div>
<div data-beta-support class="supported-logo" id="hbomax"></div> <div data-full-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="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> </div>
<a data-font="sans-bold" href="https://request.chromewatchparty.com" class="link-button" <a
>Get Chrome Extension</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>
<div class="features"> <div class="features">
<p data-font="sans-bold" class="title subtitle">FEATURES</p> <p data-font="sans-bold" class="title subtitle">FEATURES</p>
<div class="feature-card"> <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"> <p data-font="sans" class="feature-text">
Watch Party syncs play, pause and timestamp. Watch Party syncs play, pause and timestamp.
</p> </p>
</div> </div>
<div class="feature-card"> <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"> <p data-font="sans" class="feature-text">
Free group voice calls - for when text and GIFs just won't do. Free group voice calls - for when text and GIFs just won't do.
</p> </p>
</div> </div>
<div class="feature-card"> <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"> <p data-font="sans" class="feature-text">
Unlimited gifs from Giphy in chat. Unlimited gifs from Giphy in chat.
</p> </p>
</div> </div>
<div class="feature-card"> <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"> <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> </p>
</div> </div>
<div class="feature-card"> <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"> <p data-font="sans" class="feature-text">
There when you need it, hideable when you want to focus on the show. There when you need it, hideable when you want to focus on the show.
</p> </p>
</div> </div>
<div class="feature-card"> <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"> <p data-font="sans" class="feature-text">
No party crashers. Custom party names secured with custom pins. No party crashers. Custom party names secured with custom pins.
</p> </p>
</div> </div>
<div class="feature-card"> <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"> <p data-font="sans" class="feature-text">
No sign up, no login, no data taken from you. No sign up, no login, no data taken from you.
</p> </p>
</div> </div>
</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"> <footer data-font="sans">
<p class="title built-with"> <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>
<p class="title built-with"> <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://firebase.google.com/">FIREBASE</a>,
<a href="https://material-ui.com/">MATERIAL-UI</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://reactjs.org/">REACT JS</a>,
<a href="https://giphy.com/">GIPHY</a>, & <a href="https://dolby.io/">DOLBY VOXEET</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> </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 Support Watch Party
</a> </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 >Get Chrome Extension</a
> >
<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", "name": "Watch Party",
"short_name": "WP", "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 Unauthorized copying of this file, via any medium is strictly prohibited
Proprietary and confidential Proprietary and confidential
Written by Ayushya Amitabh <admin@chromewatchparty.com>, 2020 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 LOGO_INIT = 0;
var GET_INIT = 0; var GET_INIT = 0;
function handleSticky() { function handleSticky() {
const logo = document.querySelector('.logo'); const logo = document.querySelector('.logo.duplicate');
const get = document.querySelector('.landing .link-button'); const get = document.querySelector('.landing .link-button.duplicate');
if (window.scrollY >= LOGO_INIT + logo.offsetHeight * 2) { if (window.scrollY >= LOGO_INIT + logo.offsetHeight) {
logo.classList.add('stickied'); logo.classList.add('stickied');
} else { } else {
logo.classList.remove('stickied'); logo.classList.remove('stickied');
} }
if (get) { if (get) {
if (window.scrollY >= GET_INIT + get.offsetHeight * 2) { if (window.scrollY >= GET_INIT + get.offsetHeight) {
get.classList.add('stickied'); get.classList.add('stickied');
} else { } else {
get.classList.remove('stickied'); 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 () { window.onload = function () {
LOGO_INIT = document.querySelector('.logo').offsetTop; LOGO_INIT = document.querySelector('.logo').offsetTop;
GET_INIT = document.querySelector('.landing .link-button') GET_INIT = document.querySelector('.landing .link-button')
? document.querySelector('.landing .link-button').offsetTop ? document.querySelector('.landing .link-button').offsetTop
: 0; : 0;
this.handleLogoMargin();
window.addEventListener('scroll', this.handleSticky); 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 Unauthorized copying of this file, via any medium is strictly prohibited
Proprietary and confidential Proprietary and confidential
Written by Ayushya Amitabh <admin@chromewatchparty.com>, 2020 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 { 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 Unauthorized copying of this file, via any medium is strictly prohibited
Proprietary and confidential Proprietary and confidential
Written by Ayushya Amitabh <admin@chromewatchparty.com>, 2020 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 { @keyframes circleFloat {
@@ -71,6 +76,17 @@ body {
letter-spacing: 2.2rem; 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 { .logo-tag {
color: var(--primary-text); color: var(--primary-text);
font-size: 3.2rem; font-size: 3.2rem;
@@ -129,16 +145,17 @@ body {
margin: 10rem 0; margin: 10rem 0;
} }
.feature-icon { .feature-icon video {
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;
border-radius: 100%; 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 { .feature-text {
@@ -207,7 +224,7 @@ footer .link-button {
.welcome .landing#tips .tip { .welcome .landing#tips .tip {
padding: 3rem 0; padding: 3rem 0;
border-bottom: 0.5px solid var(--primary-medium); border-bottom: 0.5px solid var(--cta);
padding-right: 3rem; padding-right: 3rem;
} }
@@ -239,7 +256,7 @@ footer .link-button {
.landing .supported .supported-logo[data-full-support]::before, .landing .supported .supported-logo[data-full-support]::before,
.landing .supported .supported-logo[data-beta-support]::before, .landing .supported .supported-logo[data-beta-support]::before,
.landing .supported .supported-logo[data-upcoming-support]::before { .landing .supported .supported-logo[data-upcoming-support]::before {
content: 'FULLY SUPPORTED'; content: "FULLY SUPPORTED";
position: absolute; position: absolute;
width: 100%; width: 100%;
bottom: 0; bottom: 0;
@@ -250,16 +267,407 @@ footer .link-button {
font-size: 0.8rem; font-size: 0.8rem;
text-align: center; text-align: center;
color: var(--primary); color: var(--primary);
font-family: 'GlacialBold'; font-family: "GlacialBold";
background-color: var(--cta); background-color: var(--cta);
padding: 1rem 0; padding: 1rem 0;
} }
.landing .supported .supported-logo[data-beta-support]::before { .landing .supported .supported-logo[data-beta-support]::before {
content: 'BETA SUPPORT'; content: "BETA SUPPORT";
} }
.landing .supported .supported-logo[data-upcoming-support]::before { .landing .supported .supported-logo[data-upcoming-support]::before {
content: attr(data-upcoming-support); content: attr(data-upcoming-support);
text-transform: uppercase; 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 Unauthorized copying of this file, via any medium is strictly prohibited
Proprietary and confidential Proprietary and confidential
Written by Ayushya Amitabh <admin@chromewatchparty.com>, 2020 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'; @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 Unauthorized copying of this file, via any medium is strictly prohibited
Proprietary and confidential Proprietary and confidential
Written by Ayushya Amitabh <admin@chromewatchparty.com>, 2020 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, .features,
@@ -13,3 +18,41 @@ Written by Ayushya Amitabh <admin@chromewatchparty.com>, 2020
.landing video { .landing video {
max-width: 45%; 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 Unauthorized copying of this file, via any medium is strictly prohibited
Proprietary and confidential Proprietary and confidential
Written by Ayushya Amitabh <admin@chromewatchparty.com>, 2020 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 { html {
@@ -31,7 +36,7 @@ body {
position: relative; position: relative;
} }
.link-button::before { .link-button::before {
content: ''; content: "";
height: 100%; height: 100%;
top: 0; top: 0;
width: 0; width: 0;
@@ -61,13 +66,16 @@ body {
width: 100%; width: 100%;
left: 0; left: 0;
top: 0; top: 0;
opacity: 1;
padding: 3rem 10rem; padding: 3rem 10rem;
height: auto;
z-index: 999 !important; z-index: 999 !important;
} }
.landing .link-button.stickied { .landing .link-button.stickied {
position: fixed; position: fixed;
right: 0rem; right: 0rem;
top: 0; top: 0;
opacity: 1;
border-radius: 0; border-radius: 0;
font-size: 2rem; font-size: 2rem;
z-index: 1999 !important; z-index: 1999 !important;
@@ -111,21 +119,7 @@ body {
transition: all 0.3s ease-in; transition: all 0.3s ease-in;
position: relative; 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 { .feature-card .feature-text {
z-index: 1; z-index: 1;
display: table; display: table;
@@ -145,13 +139,19 @@ body {
color: var(--primary-light); color: var(--primary-light);
line-height: initial; line-height: initial;
opacity: 1; opacity: 1;
margin-top: -1.75rem;
} }
.feature-card:hover .feature-icon {
position: relative; .feature-icon {
top: -35%; display: flex;
left: unset; }
transform: scale(3);
margin-bottom: 3rem; .feature-card .feature-icon video {
transform: none;
}
.feature-card:hover .feature-icon video {
transform: scale(3) translateY(-50%);
} }
footer { footer {
@@ -187,9 +187,10 @@ footer .link-button {
.welcome .logo a::before, .welcome .logo a::before,
.copyright .logo a::before { .copyright .logo a::before {
content: ''; content: "";
height: 100%; height: 100%;
top: 0; top: -3px;
padding-top: 2px;
width: 0; width: 0;
position: absolute; position: absolute;
left: -1rem; left: -1rem;
@@ -218,7 +219,7 @@ footer .link-button {
} }
.welcome .landing#tips .tip { .welcome .landing#tips .tip {
padding-right: 0; padding-right: 100px;
transition: all ease-out 0.3s; transition: all ease-out 0.3s;
} }
@@ -231,7 +232,7 @@ footer .link-button {
} }
.welcome .landing .supported-logo::before { .welcome .landing .supported-logo::before {
content: 'START WATCHING'; content: "START WATCHING";
position: absolute; position: absolute;
width: 100%; width: 100%;
bottom: 0; bottom: 0;
@@ -242,7 +243,7 @@ footer .link-button {
font-size: 1.5rem; font-size: 1.5rem;
text-align: center; text-align: center;
color: var(--primary); color: var(--primary);
font-family: 'GlacialBold'; font-family: "GlacialBold";
background-color: var(--cta); background-color: var(--cta);
padding: 1rem 0; padding: 1rem 0;
} }
@@ -265,7 +266,7 @@ footer .link-button {
} }
.welcome .landing#tips .tip h6 { .welcome .landing#tips .tip h6 {
color: var(--primary-light); color: var(--primary-medium);
transition: all ease-out 0.3s; 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 Unauthorized copying of this file, via any medium is strictly prohibited
Proprietary and confidential Proprietary and confidential
Written by Ayushya Amitabh <admin@chromewatchparty.com>, 2020 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 { .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 Unauthorized copying of this file, via any medium is strictly prohibited
Proprietary and confidential Proprietary and confidential
Written by Ayushya Amitabh <admin@chromewatchparty.com>, 2020 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 { :root {
--primary: black; --primary: black;
--primary-medium: #232323; --primary-medium: #464646;
--primary-light: #454545; --primary-light: #626262;
--cta: #c0f300; --cta: #c0f300;
--primary-text: white; --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 Unauthorized copying of this file, via any medium is strictly prohibited
Proprietary and confidential Proprietary and confidential
Written by Ayushya Amitabh <admin@chromewatchparty.com>, 2020 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 { :root {
@@ -14,48 +19,32 @@ Written by Ayushya Amitabh <admin@chromewatchparty.com>, 2020
font-size: 10px; font-size: 10px;
} }
.supported-logo#netflix { .supported-logo#netflix {
background-image: url('../media/logos/Netflix.png'); background-image: url("../media/logos/Netflix.png");
} }
.supported-logo#hulu { .supported-logo#hulu {
background-image: url('../media/logos/Hulu.png'); background-image: url("../media/logos/Hulu.png");
} }
.supported-logo#disney { .supported-logo#disney {
background-image: url('../media/logos/DisneyColor.png'); background-image: url("../media/logos/DisneyColor.png");
} }
.supported-logo#prime { .supported-logo#prime {
background-image: url('../media/logos/PrimeColor.png'); background-image: url("../media/logos/PrimeColor.png");
} }
.supported-logo#youtube { .supported-logo#youtube {
background-image: url('../media/logos/YoutubeColor.png'); background-image: url("../media/logos/YoutubeColor.png");
} }
.supported-logo#hbonow { .supported-logo#hbonow {
background-image: url('../media/logos/HBONowBlack.png'); background-image: url("../media/logos/HBONowBlack.png");
} }
.supported-logo#hbomax { .supported-logo#hbomax {
background-image: url('../media/logos/HBOMax.png'); background-image: url("../media/logos/HBOMax.png");
} }
.supported-logo#peacock { .supported-logo#peacock {
background-image: url('../media/logos/PeacockColor.png'); background-image: url("../media/logos/PeacockColor.png");
} }
.supported-logo#googleplay {
.feature-icon#phone { background-image: url("../media/logos/GooglePlay.png");
background-image: url('../media/features/Phone.gif');
} }
.feature-icon#delete { .supported-logo#crunchyroll {
background-image: url('../media/features/Delete.gif'); background-image: url("../media/logos/Crunchyroll.png");
}
.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');
} }
+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 Unauthorized copying of this file, via any medium is strictly prohibited
Proprietary and confidential Proprietary and confidential
Written by Ayushya Amitabh <admin@chromewatchparty.com>, 2020 Written by Ayushya Amitabh <admin@chromewatchparty.com>, 2020
https://www.chromewatchparty.com/copyright
========================================================================= =========================================================================
ALL CONTENT ON THIS WEBSITE, UNLESS OTHERWISE CITED ARE THE SOLE ALL CONTENT ON WATCH PARTY'S WEBSITE OR IN WATCH PARTY'S CHROME EXTENSION APP, UNLESS OTHERWISE
INTELECTUAL PROPERTY OF AYUSHYA AMITABH, AND MAY NOT BE COPIED, EDITED, CITED ARE THE SOLE INTELECTUAL PROPERTY OF AYUSHYA AMITABH, AND MAY NOT BE
OR REPRODUCED WITHOUT PERMISSION. 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.
-------------------------------------------------------------------------> ------------------------------------------------------------------------->
<html> <html>
<head> <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> <script src="../scripts/Stickied.js"></script>
<title>Watch Party | Welcome</title> <title>Watch Party | Welcome</title>
</head> </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"> <body class="welcome">
<div class="landing"> <div class="landing">
<div class="logo-circle"></div> <div class="logo-circle"></div>
<div class="logo-circle"></div> <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> <h1 data-font="serif" class="logo"><a href="../">WATCH PARTY</a></h1>
<h5 data-font="sans" class="logo-tag"> <h5 data-font="sans" class="logo-tag">
WELCOME TO WATCH PARTY WELCOME TO WATCH PARTY
</h5> </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"> <p data-font="sans-bold" class="title subtitle">
WATCH TOGETHER ON WATCH TOGETHER ON
</p> </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://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.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://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>
</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"> <div id="tips" class="landing">
<p data-font="sans-bold" class="title"> <p data-font="sans-bold" class="title">
TIPS & FAQ TIPS & FAQ
</p> </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"> <div class="tip">
<h6 data-font="sans"> <h6 data-font="sans">
CAN I USE WATCH PARTY IN FULLSCREEN 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. If you started the party, you can simply join the party from a different place to change the party location.
</p> </p>
</div> </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"> <div class="tip">
<h6 data-font="sans"> <h6 data-font="sans">
CHAT NOTIFICATIONS ARE ANNOYING 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 CAN I DISABLE WATCH PARTY TEMPORARILY
</h6> </h6>
<p data-font="sans-bold"> <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> </p>
</div> </div>
<a data-font="sans-bold" href="mailto:admin@chromewatchparty.com?subject=New FAQ Question" class="link-button"> <a data-font="sans-bold" href="mailto:admin@chromewatchparty.com?subject=New FAQ Question" class="link-button">