diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..31db3b2 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,4 @@ +# These are supported funding model platforms + +github: bra1n +custom: https://www.paypal.me/bra1n diff --git a/README.md b/README.md index 6193849..73c403f 100644 --- a/README.md +++ b/README.md @@ -30,3 +30,8 @@ It is supposed to aid storytellers and allow them to quickly set up and capture * All other images and icons are copyright to their respective owners This project and its website are provided free of charge and not affiliated with The Pandemonium Institute in any way. + +## Donations +This project will always be available free of charge, since I love building cool things and playing Blood on the Clocktower. If you still want to support me with a donation, you can do that here: + +[![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.me/bra1n) diff --git a/package-lock.json b/package-lock.json index 96e2a39..88968a6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -40,6 +40,14 @@ "@fortawesome/fontawesome-common-types": "^0.2.28" } }, + "@fortawesome/free-brands-svg-icons": { + "version": "5.13.0", + "resolved": "https://registry.npmjs.org/@fortawesome/free-brands-svg-icons/-/free-brands-svg-icons-5.13.0.tgz", + "integrity": "sha512-/6xXiJFCMEQxqxXbL0FPJpwq5Cv6MRrjsbJEmH/t5vOvB4dILDpnY0f7zZSlA8+TG7jwlt12miF/yZpZkykucA==", + "requires": { + "@fortawesome/fontawesome-common-types": "^0.2.28" + } + }, "@fortawesome/free-solid-svg-icons": { "version": "5.13.0", "resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-5.13.0.tgz", diff --git a/package.json b/package.json index dff4f2c..ec34f8e 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,7 @@ "main": "App.vue", "dependencies": { "@fortawesome/fontawesome-svg-core": "^1.2.28", + "@fortawesome/free-brands-svg-icons": "^5.13.0", "@fortawesome/free-solid-svg-icons": "^5.13.0", "@fortawesome/vue-fontawesome": "^0.1.9", "@vue/cli-service": "^4.3.1", diff --git a/server/index.js b/server/index.js index a61a557..7c9cd4a 100644 --- a/server/index.js +++ b/server/index.js @@ -7,17 +7,24 @@ const server = https.createServer({ key: fs.readFileSync("key.pem") }); const wss = new WebSocket.Server({ - server, - // port: 8081, + ...(process.env.NODE_ENV === "development" ? { port: 8081 } : { server }), verifyClient: info => !!info.origin.match(/^https?:\/\/(bra1n\.github\.io|localhost)/i) }); +function noop() {} + +function heartbeat() { + this.isAlive = true; +} + wss.on("connection", function connection(ws, req) { ws.channel = req.url .split("/") .pop() .toLocaleLowerCase(); + ws.isAlive = true; + ws.on("pong", heartbeat); ws.on("message", function incoming(data) { if (!data.match(/^\["ping/i)) { console.log(ws.channel, wss.clients.size, data); @@ -34,4 +41,18 @@ wss.on("connection", function connection(ws, req) { }); }); -server.listen(8080); +const interval = setInterval(function ping() { + wss.clients.forEach(function each(ws) { + if (ws.isAlive === false) return ws.terminate(); + ws.isAlive = false; + ws.ping(noop); + }); +}, 30000); + +wss.on("close", function close() { + clearInterval(interval); +}); + +if (process.env.NODE_ENV !== "development") { + server.listen(8080); +} diff --git a/src/App.vue b/src/App.vue index a27f522..574691a 100644 --- a/src/App.vue +++ b/src/App.vue @@ -62,6 +62,12 @@ export default { case "a": this.$refs.menu.addPlayer(); break; + case "h": + this.$refs.menu.hostSession(); + break; + case "j": + this.$refs.menu.joinSession(); + break; case "r": this.$store.commit("toggleModal", "reference"); break; @@ -73,8 +79,8 @@ export default { if (this.session.isSpectator) return; this.$store.commit("toggleModal", "roles"); break; - case "Escape": - this.$store.commit("toggleMenu"); + case "escape": + this.$store.commit("toggleModal"); } } } diff --git a/src/components/Menu.vue b/src/components/Menu.vue index f08e68e..79ea96d 100644 --- a/src/components/Menu.vue +++ b/src/components/Menu.vue @@ -24,80 +24,99 @@
@@ -127,6 +168,11 @@ export default { ...mapState(["grimoire", "session"]), ...mapState("players", ["players"]) }, + data() { + return { + tab: "grimoire" + }; + }, methods: { takeScreenshot(dimensions = {}) { this.$store.commit("updateScreenshot"); @@ -261,7 +307,7 @@ export default { margin-left: 10px; } - .session { + span.session { color: $demon; &.spectator { color: $townsfolk; @@ -270,8 +316,8 @@ export default { } .menu { - width: 210px; - transform-origin: 190px 22px; + width: 220px; + transform-origin: 200px 22px; transition: transform 500ms cubic-bezier(0.68, -0.55, 0.27, 1.55); transform: rotate(-90deg); position: absolute; @@ -294,6 +340,14 @@ export default { padding: 5px 5px 15px; } + a { + color: white; + text-decoration: none; + &:hover { + color: red; + } + } + ul { display: flex; list-style-type: none; @@ -306,16 +360,48 @@ export default { border-radius: 10px 0 10px 10px; li { - padding: 2px 10px; + padding: 2px 5px; color: white; text-align: left; background: rgba(0, 0, 0, 0.7); + &.tabs { + display: flex; + padding: 0; + svg { + flex-grow: 1; + flex-shrink: 0; + height: 35px; + border-bottom: 3px solid black; + border-right: 3px solid black; + padding: 5px 0; + cursor: pointer; + transition: color 250ms; + &:hover { + color: red; + } + &:last-child { + border-right: 0; + } + } + &.grimoire .fa-book-open, + &.players .fa-users, + &.characters .fa-theater-masks, + &.session .fa-broadcast-tower, + &.help .fa-question { + background: linear-gradient( + to bottom, + $townsfolk 0%, + rgba(0, 0, 0, 0.5) 100% + ); + } + } + &:last-child { margin-bottom: 0; } - &:not(.headline):hover { + &:not(.headline):not(.tabs):hover { cursor: pointer; color: red; } @@ -332,7 +418,7 @@ export default { .headline { font-family: PiratesBay, sans-serif; letter-spacing: 1px; - padding: 5px 10px; + padding: 0 10px; text-align: center; background: linear-gradient( to right, diff --git a/src/components/modals/EditionModal.vue b/src/components/modals/EditionModal.vue index 4f29304..84b6742 100644 --- a/src/components/modals/EditionModal.vue +++ b/src/components/modals/EditionModal.vue @@ -86,6 +86,10 @@ export default { [ "Frankenstein's Mayor by Ted", "https://gist.githubusercontent.com/bra1n/32c52b422cc01b934a4291eeb81dbcee/raw/3ca5a043c41141ac40667dc15097deb327263268/Frankensteins_Mayor_by_Ted.json" + ], + [ + "Vigormortis High School", + "https://gist.githubusercontent.com/bra1n/1f65bd4a999524719d5dabe98c3c2d27/raw/f28d3268846c182b2078888122003c6f95c6b2cf/VigormortisHighSchool.json" ] ] }; diff --git a/src/main.js b/src/main.js index c8c2925..1b30071 100644 --- a/src/main.js +++ b/src/main.js @@ -3,6 +3,7 @@ import App from "./App"; import store from "./store"; import { library } from "@fortawesome/fontawesome-svg-core"; import { fas } from "@fortawesome/free-solid-svg-icons"; +import { fab } from "@fortawesome/free-brands-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome"; const faIcons = [ @@ -13,12 +14,15 @@ const faIcons = [ "CheckSquare", "Cog", "Copy", + "Dice", "ExchangeAlt", "FileUpload", "HandPointRight", "Heartbeat", + "Image", "Link", "PeopleArrows", + "Question", "Random", "RedoAlt", "SearchMinus", @@ -28,6 +32,7 @@ const faIcons = [ "TheaterMasks", "Times", "TimesCircle", + "TrashAlt", "Undo", "User", "UserEdit", @@ -35,7 +40,11 @@ const faIcons = [ "Users", "VoteYea" ]; -library.add(...faIcons.map(i => fas["fa" + i])); +const fabIcons = ["Github", "Discord"]; +library.add( + ...faIcons.map(i => fas["fa" + i]), + ...fabIcons.map(i => fab["fa" + i]) +); Vue.component("font-awesome-icon", FontAwesomeIcon); Vue.config.productionTip = false; diff --git a/src/store/index.js b/src/store/index.js index 9d9ec1f..4298c95 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -79,12 +79,12 @@ export default new Vuex.Store({ } }, toggleModal({ modals }, name) { - modals[name] = !modals[name]; - if (modals[name]) { - for (let modal in modals) { - if (modal === name) continue; - modals[modal] = false; - } + if (name) { + modals[name] = !modals[name]; + } + for (let modal in modals) { + if (modal === name) continue; + modals[modal] = false; } }, updateScreenshot({ grimoire }, status) {