From 74b263575574e19ab57385fd5a287543659005b6 Mon Sep 17 00:00:00 2001 From: Victor Westerlund Date: Mon, 8 Feb 2021 04:50:47 +0100 Subject: [PATCH] New folder structure for extension Separated client- and server-side features into seperate root folders. Since Stadia Avatar now has two versions (Userscript and Chrome extension). Added core extension functionality. Created a page constructor for extension popup. High probability that I will create a seperate repo for this feature, as it's pretty neat and very useful for future extensions. --- .gitignore | 2 + .../extension/StadiaAvatar.js | 0 client/extension/_locales/en/messages.json | 18 ++ client/extension/assets/css/popup.css | 163 ++++++++++++++++++ .../assets/img/icon_set_gravatar.svg | 1 + client/extension/assets/img/icon_set_url.svg | 1 + client/extension/assets/img/logo.png | Bin 0 -> 7079 bytes client/extension/assets/img/logo_icon_128.png | Bin 0 -> 5324 bytes client/extension/assets/img/logo_icon_16.png | Bin 0 -> 480 bytes client/extension/assets/img/logo_icon_32.png | Bin 0 -> 965 bytes client/extension/assets/img/logo_icon_48.png | Bin 0 -> 1548 bytes client/extension/assets/js/popup.js | 33 ++++ .../assets/js/popup_modules/ChangeAvatar.mjs | 12 ++ .../assets/js/popup_modules/Page.mjs | 70 ++++++++ client/extension/manifest.json | 26 +++ client/extension/popup.html | 22 +++ client/userscript-client.js | 132 ++++++++++++++ {classes => server/classes}/Database.php | 0 {classes => server/classes}/Gravatar.php | 0 {classes => server/classes}/Message.php | 0 {endpoint => server/endpoint}/get.php | 0 {endpoint => server/endpoint}/update.php | 0 userscript-bot.js => server/userscript-bot.js | 0 23 files changed, 480 insertions(+) create mode 100644 .gitignore rename userscript-client.js => client/extension/StadiaAvatar.js (100%) create mode 100644 client/extension/_locales/en/messages.json create mode 100644 client/extension/assets/css/popup.css create mode 100644 client/extension/assets/img/icon_set_gravatar.svg create mode 100644 client/extension/assets/img/icon_set_url.svg create mode 100644 client/extension/assets/img/logo.png create mode 100644 client/extension/assets/img/logo_icon_128.png create mode 100644 client/extension/assets/img/logo_icon_16.png create mode 100644 client/extension/assets/img/logo_icon_32.png create mode 100644 client/extension/assets/img/logo_icon_48.png create mode 100644 client/extension/assets/js/popup.js create mode 100644 client/extension/assets/js/popup_modules/ChangeAvatar.mjs create mode 100644 client/extension/assets/js/popup_modules/Page.mjs create mode 100644 client/extension/manifest.json create mode 100644 client/extension/popup.html create mode 100644 client/userscript-client.js rename {classes => server/classes}/Database.php (100%) rename {classes => server/classes}/Gravatar.php (100%) rename {classes => server/classes}/Message.php (100%) rename {endpoint => server/endpoint}/get.php (100%) rename {endpoint => server/endpoint}/update.php (100%) rename userscript-bot.js => server/userscript-bot.js (100%) diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2deaabd --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +*.crx +*.pem \ No newline at end of file diff --git a/userscript-client.js b/client/extension/StadiaAvatar.js similarity index 100% rename from userscript-client.js rename to client/extension/StadiaAvatar.js diff --git a/client/extension/_locales/en/messages.json b/client/extension/_locales/en/messages.json new file mode 100644 index 0000000..4ac2f45 --- /dev/null +++ b/client/extension/_locales/en/messages.json @@ -0,0 +1,18 @@ +{ + "extension_description": { + "message": "Custom Stadia avatars for you and your friends", + "description": "Short summary of this extension's function." + }, + "avatar_set": { + "message": "Set avatar from", + "description": "List of different options to set a users's avatar" + }, + "avatar_set_url_support": { + "message": "All image formats supported by Chrome except SVG can be added", + "description": "Disclaimer about supported image formats" + }, + "page_return": { + "message": "Go back", + "description": "Tooltip when hovering the back button on a page" + } +} \ No newline at end of file diff --git a/client/extension/assets/css/popup.css b/client/extension/assets/css/popup.css new file mode 100644 index 0000000..e8491cb --- /dev/null +++ b/client/extension/assets/css/popup.css @@ -0,0 +1,163 @@ +:root { + /* color components */ + --palette-background: 33,33,33; + --palette-contrast: 255,255,255; + --palette-header: 45,45,45; + --palette-accent-high: 253,74,24; + --palette-accent-low: 170,3,88; + + /* compiled colors */ + --color-background: rgb(var(--palette-background)); + --color-contrast: rgb(var(--palette-contrast)); + --color-header: rgb(var(--palette-header)); + --color-accent: linear-gradient(145deg, var(--palette-accent-high) 0%, var(--palette-accent-low) 100%); +} + +* { + margin: 0; + color: var(--color-contrast); +} + +*::-webkit-scrollbar { + display: none; +} + +html, +body { + background-color: var(--color-background); + display: flex; + flex-direction: column; + align-items: center; +} + +body { + --page-depth: 0; + --animation-speed: 400ms; + + width: 400px; + transition: var(--animation-speed) transform cubic-bezier(0.4, 0, 0.2, 1); + transform: translateX(calc((100% * var(--page-depth)) * -1)); +} + +.page .header, +header { + width: 100%; + background-color: var(--color-header); + border-bottom: solid 1px black; +} + +header { + --header-padding: 30px; + --pfp-size: 80px; + --pfp-stroke-offset: 5px; + --pfp-stroke-width: 4px; + --pfp-stroke-size: calc(var(--pfp-stroke-offset) + var(--pfp-stroke-width)); + + height: calc(var(--pfp-size) + (var(--pfp-stroke-size) * 2)); + padding: var(--header-padding) 0 var(--header-padding) 0; + display: flex; + justify-content: center; + align-items: center; +} + +#myAvatar { + width: var(--pfp-size); + height: var(--pfp-size); + box-shadow: + 0 0 0 var(--pfp-stroke-offset) var(--color-header), + 0 0 0 var(--pfp-stroke-size) rgb(var(--palette-accent-high)); + border-radius: 100%; +} + +ol { + list-style-type: none; + width: 100%; + font-size: 15px; + padding-left: 0; +} + +ol li { + --padding: 25px; + + width: calc(100% - (var(--padding) * 2)); + height: 64px; + padding: 0 var(--padding) 0 var(--padding); + display: flex; + align-items: center; + cursor: pointer; +} + +.page .back:hover, +ol li:hover { + background: rgba(var(--palette-contrast),.1); +} + +ol li img { + height: 20px; + filter: brightness(0) invert(1); + margin-right: 20px; +} + +/* ---- */ + +.page { + transition: var(--animation-speed) opacity; + position: absolute; + top: 0; + left: 100%; + width: 100%; + opacity: 1; +} + +.page .header { + --height: 60px; + --button-size: 40px; + --padding: 10px; + + height: 60px; + box-sizing: border-box; + padding: 0 var(--padding) 0 var(--padding); + display: flex; + align-items: center; +} + +.page .back { + --chevron-size: 10px; + --chevron-stroke-width: 3px; + + width: var(--button-size); + height: var(--button-size); + border-radius: 4px; + border: solid 1px var(--color-background); + cursor: pointer; + display: flex; + justify-content: center; + align-items: center; +} + +.page .back::before { + content: ''; + border-style: solid; + border-width: var(--chevron-stroke-width) var(--chevron-stroke-width) 0 0; + display: inline-block; + width: var(--chevron-size); + height: var(--chevron-size); + top: 0; + left: calc(var(--chevron-size) / 3); + transform: rotate(-135deg); + position: relative; + vertical-align: top; +} + +.page .header p { + width: calc(100% - (var(--button-size) * 2)); + font-size: 17px; + text-align: center; +} + +.page .body { + position: relative; + top: var(--height); + box-sizing: border-box; + padding: 20px; +} \ No newline at end of file diff --git a/client/extension/assets/img/icon_set_gravatar.svg b/client/extension/assets/img/icon_set_gravatar.svg new file mode 100644 index 0000000..b14cd67 --- /dev/null +++ b/client/extension/assets/img/icon_set_gravatar.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/client/extension/assets/img/icon_set_url.svg b/client/extension/assets/img/icon_set_url.svg new file mode 100644 index 0000000..633692c --- /dev/null +++ b/client/extension/assets/img/icon_set_url.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/client/extension/assets/img/logo.png b/client/extension/assets/img/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..89d3e4b35ef3d5e5c96d2e926b4604aec2c630f9 GIT binary patch literal 7079 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 Rco6^q002ovPDHLkV1kLw-{AlN literal 0 HcmV?d00001 diff --git a/client/extension/assets/img/logo_icon_128.png b/client/extension/assets/img/logo_icon_128.png new file mode 100644 index 0000000000000000000000000000000000000000..eabf0228eda65d3a74514e062dabe0839b44c09e GIT binary patch literal 5324 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 literal 0 HcmV?d00001 diff --git a/client/extension/assets/img/logo_icon_48.png b/client/extension/assets/img/logo_icon_48.png new file mode 100644 index 0000000000000000000000000000000000000000..2a6361670c9ddb78beff80c5fa9222794c2c92c5 GIT binary patch literal 1548 zcmV+n2J`ueP)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!TBZ2*oD1}-#=&O>^ZwzKx1NK&X@f8|M_q8&p$Kg z>{;z^g9U3-ll+VBxdp#R+x){Nb~(3?v7gdM?{FnF$>a^kWZSu2RiZ+%7TRfB&K>OTiqZSOhG{{=Yg^O&wsDuZlPH-VR zH?jFAw(|#1O(fh8GtQ+PN=MKn`=in;GKG+7DdZHHFpFm?=$eJ>mYI!N;xhYo?BNJF zf`FDsC(c^zt!o=N`?<6b$H2&}!BHz5J;%IsOl zfT8Tix4@oEiD)uakcxxeM|{OgiCfL!7?FYXp^)<%&Y2j4uiG_wW=^_V(6;_CCm(jB zJU@WS-D-Afg_4O2S+#<*?BcPm@t*W55p1*~W(DS08#Zj%xEH{dYSTW)1M8@IP+7R; z5zLxWdk2qQD=KNkL$6ucV&kODVgI=N`pMTEt4vNjNEOLoVYB#KWX2%BdUjLY>ujMq zLp*wuPG4K`-LP8V*Z2(-wGoqDXLqVHxvbzgD*KV-n2I9McV-Ut49IxxqPOCHv?&#~ z`@&%-e8dWsKaHEfYcLM8+5OqyL)fd5M&Gm6AWYJcd-?qS;hc(U*xHr9**C#EI=o_A z#tL`8h~%GAP`}2h&Z0aLa9prlW z-cd8lY#1c0xhVtvXqGaZVydC@n_+^rfRv~k=(|64Rgf*t5pFn=;;f_%`{k?;Z&2?>9 zRE?~Y7O_a*O&?CdGBam(QzR5*sA%&CB9whU57y(l$GEqAeV43EkF-QaC|s>2)qD9TrE0; zW4JT#O7|toj}OJKV5=v)@Xf-l8uQ$CWm<0 z=#JcKt|h_N0Lr!aY3%8u(XQ71%u2erS3zFG#+e^+ZzJ9ZS?++9tU7T-=|S@mC`0J< zI>Pkb8o+>VdQ6~kt_?De<~VSKK5yi@#R(vx0w6t-sQoyNkUH| { + return key ? chrome.i18n.getMessage(key) : ""; + }); + + if(valNewH != valStrH) { + element.innerHTML = valNewH; + } + } +} + +function eventHandler(event) { + const target = event.target.closest("[button]"); + switch(target.getAttribute("button")) { + case "avatar:url": new AvatarURL(); break; + } +} + +document.addEventListener("DOMContentLoaded", function () { + // Bind click listeners to all button attributes + for(const button of document.querySelectorAll("[button]")) { + button.addEventListener("click",event => eventHandler(event)); + } +}); + +localizePage(); \ No newline at end of file diff --git a/client/extension/assets/js/popup_modules/ChangeAvatar.mjs b/client/extension/assets/js/popup_modules/ChangeAvatar.mjs new file mode 100644 index 0000000..86752f4 --- /dev/null +++ b/client/extension/assets/js/popup_modules/ChangeAvatar.mjs @@ -0,0 +1,12 @@ +import { Page } from "./Page.mjs"; + +export class AvatarURL extends Page { + + constructor() { + super(chrome.i18n.getMessage("avatar_set") + " URL"); + + this.appendHTML(`

${chrome.i18n.getMessage("avatar_set_url_support")}

`); + this.open(); + } + +} diff --git a/client/extension/assets/js/popup_modules/Page.mjs b/client/extension/assets/js/popup_modules/Page.mjs new file mode 100644 index 0000000..fad30c9 --- /dev/null +++ b/client/extension/assets/js/popup_modules/Page.mjs @@ -0,0 +1,70 @@ +export class Page { + + constructor(title = "") { + this.body = null; + + this.pageDepth = () => { + return parseInt(getComputedStyle(document.body).getPropertyValue("--page-depth")); + } + + this.create(title); + } + + create(title) { + // Create elements + const wrapper = document.createElement("div"); + this.body = document.createElement("div"); + const header = document.createElement("div"); + const backButton = document.createElement("div"); + + const pageDepth = (this.pageDepth() + 1) * 100; + + // Add element attributes + wrapper.classList.add("page"); + wrapper.style.setProperty("left",pageDepth + "%"); + this.body.classList.add("body"); + header.classList.add("header"); + backButton.classList.add("back"); + backButton.setAttribute("title",chrome.i18n.getMessage("page_return")); + + // Attach interfaces + wrapper.close = () => this.close(); // Attach public interface to close this page + backButton.addEventListener("click",() => this.close()); + + // Append document subtree + header.appendChild(backButton); + header.insertAdjacentHTML("beforeend",`

${title}

`); + wrapper.appendChild(header); + wrapper.appendChild(this.body); + document.body.appendChild(wrapper); + } + + destroy() { + const wrapper = this.body.closest(".page"); + + while(wrapper.firstChild) { + wrapper.removeChild(wrapper.lastChild); + } + wrapper.remove(); + } + + appendHTML(HTML) { + this.body.insertAdjacentHTML("beforeend",HTML); + } + + // ---- + + close() { + const delay = parseInt(getComputedStyle(document.body).getPropertyValue("--animation-speed")); + document.body.style.setProperty("--page-depth",this.pageDepth() - 1); + + setTimeout(() => { + this.destroy(); + },delay); + } + + open() { + document.body.style.setProperty("--page-depth",this.pageDepth() + 1); + } + +} diff --git a/client/extension/manifest.json b/client/extension/manifest.json new file mode 100644 index 0000000..8c55508 --- /dev/null +++ b/client/extension/manifest.json @@ -0,0 +1,26 @@ +{ + "name": "Stadia Avatar", + "version": "1.0.1", + "author": "Victor Westerlund", + "default_locale": "en", + "homepage_url": "https://github.com/VictorWesterlund/stadia-avatar", + "description": "__MSG_extension_description__", + "icons": { + "16": "assets/img/logo_icon_16.png", + "32": "assets/img/logo_icon_32.png", + "48": "assets/img/logo_icon_48.png", + "128": "assets/img/logo_icon_128.png" + }, + "host_permissions": [ + "*://stadia.google.com/*" + ], + "action": { + "default_popup": "popup.html", + "default_title": "__MSG_extension_description__", + "default_icon": { + "16": "assets/img/logo_icon_16.png", + "32": "assets/img/logo_icon_32.png" + } + }, + "manifest_version": 3 +} \ No newline at end of file diff --git a/client/extension/popup.html b/client/extension/popup.html new file mode 100644 index 0000000..c74a023 --- /dev/null +++ b/client/extension/popup.html @@ -0,0 +1,22 @@ + + + + + + +
+ +
+
    +
  1. + +

    __MSG_avatar_set__ URL

    +
  2. +
  3. + +

    __MSG_avatar_set__ Gravatar

    +
  4. +
+ + + \ No newline at end of file diff --git a/client/userscript-client.js b/client/userscript-client.js new file mode 100644 index 0000000..18d80fa --- /dev/null +++ b/client/userscript-client.js @@ -0,0 +1,132 @@ +// ==UserScript== +// @name Stadia Avatars +// @namespace https://victorwesterlund.com/ +// @version 1.0 +// @description victorWesterlund/stadia-avatar +// @author VictorWesterlund +// @match https://stadia.google.com/* +// @grant none +// @noframes +// ==/UserScript== + +(function() { + 'use strict'; + + const stadiaAvatar = new URL("https://api.victorwesterlund.com/stadia-avatar/get"); + const gravatar = new URL("https://www.gravatar.com/"); + + /* + G: Suitable for display on all websites with any audience type. + PG: May contain rude gestures, provocatively dressed individuals, the lesser swear words, or mild violence. + R: May contain such things as harsh profanity, intense violence, nudity, or hard drug use. + X: May contain hardcore sexual imagery or extremely disturbing violence. + */ + gravatar.searchParams.set("rating","G"); + + // Stylesheet for Stadia Avatars + class StadiaAvatarCSS { + + constructor() { + this.sheet = null; + this.createStylesheet(); + } + + createStylesheet() { + const style = document.createElement("style"); + style.setAttribute("data-stadia-avatars",""); + style.setAttribute("data-late-css",""); + + document.head.appendChild(style); + this.sheet = style.sheet; + } + + // Serialized group of selectors based on context + selectors(group,id = false) { + switch(group) { + case "me": return ` + .ksZYgc, + .rybUIf + `; + case "friends": return ` + c-wiz[data-p='%.@.null,"${id}"]'] .drvCDc, + .Y1rZWd[data-player-id="${id}"] .Fnd1Pd, + .Y1rZWd[data-playerid="${id}"] .Fnd1Pd, + .w2Sl7c[data-playerid="${id}"] .drvCDc + `; + } + } + + add(selectors,avatar) { + this.sheet.insertRule(`${selectors} { background-image: url(${avatar}) !important; }`); + } + + } + + const avatars = new StadiaAvatarCSS(); + + // ---- + + // Return the player ID attribute of an element + const getID = (target) => { + const id = target.getAttribute("data-player-id") ?? target.getAttribute("data-playerid"); + return id; + } + + async function getStadiaAvatar(playerID) { + stadiaAvatar.searchParams.set("userID",playerID); + + const response = await fetch(stadiaAvatar); + return response.json(); + } + + // Fetch avatar and append to stylesheet + function replaceWithGravatar(group,playerID) { + getStadiaAvatar(playerID).then(response => { + if(response.status !== "OK") { + return false; + } + + gravatar.pathname = "/avatar/" + response.avatar; // Append Gravatar hash + avatars.add(avatars.selectors(group,playerID),gravatar); // Add style override by group + }).catch( + // Ignore missing avatars + ); + } + + // ---- + + replaceWithGravatar("me",getID(document.querySelector("[jsname='HiaYvf']"))); + + function updateFamily(group,wrapper) { + for(const element of wrapper) { + const id = getID(element); + if(!id) { + continue; + } + + replaceWithGravatar(group,id); + } + } + + let timeout = null; + + const friendsList = (mutation,observer) => { + clearTimeout(timeout); + + timeout = setTimeout(() => { + let elements = []; + elements = Array.prototype.concat.apply(elements,document.querySelector("[jsaction='JIbuQc:mbLu7b']").children); + elements = Array.prototype.concat.apply(elements,document.querySelector("[jsname='FhFdCc']").children); + + updateFamily("friends",elements); + },700); + } + + const friendsMenu = document.querySelector("[jsname='TpfyL']"); + const friends = new MutationObserver(friendsList); + friends.observe(friendsMenu,{ + childList: true, + subtree: true + }); + +})(); \ No newline at end of file diff --git a/classes/Database.php b/server/classes/Database.php similarity index 100% rename from classes/Database.php rename to server/classes/Database.php diff --git a/classes/Gravatar.php b/server/classes/Gravatar.php similarity index 100% rename from classes/Gravatar.php rename to server/classes/Gravatar.php diff --git a/classes/Message.php b/server/classes/Message.php similarity index 100% rename from classes/Message.php rename to server/classes/Message.php diff --git a/endpoint/get.php b/server/endpoint/get.php similarity index 100% rename from endpoint/get.php rename to server/endpoint/get.php diff --git a/endpoint/update.php b/server/endpoint/update.php similarity index 100% rename from endpoint/update.php rename to server/endpoint/update.php diff --git a/userscript-bot.js b/server/userscript-bot.js similarity index 100% rename from userscript-bot.js rename to server/userscript-bot.js