diff --git a/client/index.html b/client/index.html index b1f87157..b32ef146 100644 --- a/client/index.html +++ b/client/index.html @@ -10,7 +10,7 @@ - Aran blog + Dragonroll
diff --git a/client/public/sounds/click.wav b/client/public/sounds/click.wav new file mode 100644 index 00000000..8eaf3561 Binary files /dev/null and b/client/public/sounds/click.wav differ diff --git a/client/public/sounds/close.wav b/client/public/sounds/close.wav new file mode 100644 index 00000000..85601bb3 Binary files /dev/null and b/client/public/sounds/close.wav differ diff --git a/client/public/sounds/roll1.wav b/client/public/sounds/roll1.wav new file mode 100644 index 00000000..5be8aa46 Binary files /dev/null and b/client/public/sounds/roll1.wav differ diff --git a/client/public/sounds/roll2.wav b/client/public/sounds/roll2.wav new file mode 100644 index 00000000..3acdee83 Binary files /dev/null and b/client/public/sounds/roll2.wav differ diff --git a/client/public/sounds/snap.wav b/client/public/sounds/snap.wav new file mode 100644 index 00000000..0739c573 Binary files /dev/null and b/client/public/sounds/snap.wav differ diff --git a/client/src/assets/main.css b/client/src/assets/main.css index f28f8d1c..67762030 100644 --- a/client/src/assets/main.css +++ b/client/src/assets/main.css @@ -45,6 +45,21 @@ a { margin: 4.25px; } +.buttons-row { + width: 100%; + padding-right: 10px; + padding-left: 10px; + display: flex; + flex-direction: row; + justify-content: center; +} + +.button-row { + margin-left: 5px; + margin-right: 5px; + flex-grow: 1; +} + hr { border: 0; height: 1px; @@ -89,7 +104,8 @@ input[type=text]:focus, input[type=password]:focus, input[type=email]:focus { button { margin-top: 5px; margin-bottom: 5px; - padding: 18px; + + padding: 14px; font-size: 15px; border-radius: 6px; outline: none; diff --git a/client/src/services/Dragonroll.js b/client/src/services/Dragonroll.js new file mode 100644 index 00000000..0522fe68 --- /dev/null +++ b/client/src/services/Dragonroll.js @@ -0,0 +1,12 @@ +import { ClearAll, ClearWindow, CreateWindow } from './Windows'; + +function DisplayCampaign(data){ + ClearAll(); + CreateWindow('campaign_preview', { + title: data.name + }); +} + +export { + DisplayCampaign +}; \ No newline at end of file diff --git a/client/src/services/Windows.js b/client/src/services/Windows.js index ee7fd2ae..061b903d 100644 --- a/client/src/services/Windows.js +++ b/client/src/services/Windows.js @@ -7,9 +7,66 @@ const windows = { main_menu: ref([]), edit_profile: ref([]), account_settings: ref([]), - db_window: ref([]) + db_window: ref([]), + campaign_list: ref([]), + new_campaign: ref([]), + join_campaign: ref([]), + campaign_preview: ref([]) }; +const defValues = { + 'login': { + id: 'login', + title: 'Login' + }, + 'register': { + id: 'register', + title: 'Register' + }, + 'main_menu': { + id: 'main_menu', + title: "DragonRoll" + }, + 'edit_profile': { + id: 'edit_profile', + title: "Edit Profile", + close: true + }, + 'account_settings': { + id: 'account_settings', + title: "Dragonroll settings", + close: true + }, + 'campaign_list': { + id: 'campaign_list', + title: 'Campaigns', + back: () => { + ClearWindow('campaign_list'); + CreateWindow('main_menu'); + } + }, + 'new_campaign': { + id: 'new_campaign', + title: 'Create campaign', + parent: 'campaign_list', + close: true + }, + 'join_campaign': { + id: 'join_campaign', + title: 'Join campaign', + parent: 'campaign_list', + close: true + }, + 'campaign_preview': { + id: 'campaign_preview', + title: "Campaign Preview", + back: () => { + ClearWindow('campaign_preview'); + CreateWindow('campaign_list') + } + } +} + const reload = ref(0); let ReloadRef = () => { return reload }; @@ -107,35 +164,57 @@ function ResetPosition(id, pos){ } -function CreateWindow(data){ - if(windows[data.type] === undefined){ - console.error("Window type " + data.type + " is not defined!"); +function CreateWindow(type, data = {}){ + let finalData = {...{type}, ...defValues[type], ...data} + + console.log(finalData); + + if(windows[finalData.type] === undefined){ + console.error("Window type " + finalData.type + " is not defined!"); return; } let contains = false; - for (let i = 0; i < windows[data.type].length; i++) { - if(windows[data.type][i].id == data.id){ + for (let i = 0; i < windows[finalData.type].value.length; i++) { + if(windows[finalData.type].value[i].id == finalData.id){ contains = true; break; } } - if(!contains) { - windows[data.type].value.push(data); + windows[finalData.type].value.push(finalData); // reload.value += 1; - setTimeout(() => SetOnTop(data.id), 0); + setTimeout(() => SetOnTop(finalData.id), 0); } } +function CreateChildWindow(parentId, type, data = {}){ + let finalData = {...{type}, ...defValues[type], ...data} + + let parent = GetWindowWithId(parentId); + if(parent.children) parent.children.push(finalData.id); + else parent.children = [finalData.id]; + CreateWindow(type, data); +} + +function ClearAll(){ + Object.keys(windows).forEach((key) => { + windows[key].value = []; + }); +} + function ClearWindows(data){ - windows[data.type].value = []; + for (let i = 0; i < windows[data.type].value.length; i++) { + ClearWindow(windows[data.type].value[i].id); + } // reload.value += 1; } function ClearWindow(id){ let win = GetWindowWithId(id); + if(!win) return; + if(win.children) for(let i = 0; i < win.children.length; i++) ClearWindow(win.children[i]); windows[win.type].value = windows[win.type].value.filter((e) => {return e.id !== id}); // reload.value += 1; } @@ -174,7 +253,9 @@ export { ReloadRef, ClearWindows, CreateWindow, + CreateChildWindow, GetWindowWithId, SaveWindowPos, - ClearWindow + ClearWindow, + ClearAll } \ No newline at end of file diff --git a/client/src/views/HomeView.vue b/client/src/views/HomeView.vue index 25fff0d7..99e710de 100644 --- a/client/src/views/HomeView.vue +++ b/client/src/views/HomeView.vue @@ -9,18 +9,10 @@ import { CreateWindow } from '@/services/Windows' onMounted(() => { if(GetUser()){ - CreateWindow({ - type: "main_menu", - id: "main_menu", - title: "Dragonroll" - }) + CreateWindow('main_menu') return; } - CreateWindow({ - type: "login", - id: "login", - title: "Login" - }); + CreateWindow('login'); } ); diff --git a/client/src/views/RegisterView.vue b/client/src/views/RegisterView.vue deleted file mode 100644 index 4f54763f..00000000 --- a/client/src/views/RegisterView.vue +++ /dev/null @@ -1,83 +0,0 @@ - - - - - - diff --git a/client/src/views/managers/WindowManager.vue b/client/src/views/managers/WindowManager.vue index 7c7832e2..5896dc50 100644 --- a/client/src/views/managers/WindowManager.vue +++ b/client/src/views/managers/WindowManager.vue @@ -11,6 +11,10 @@ import AccountSettingsWindow from '../windows/AccountSettingsWindow.vue' import { Windows, ReloadRef } from '@/services/Windows'; import DbWindow from '../windows/database/DbWindow.vue' +import CampaignListWindow from '../windows/campaigns/CampaignListWindow.vue' +import NewCampaignWindow from '../windows/campaigns/NewCampaignWindow.vue' +import JoinCampaignWindow from '../windows/campaigns/JoinCampaignWindow.vue' +import CampaignPreviewWindow from '@/views/windows/campaigns/CampaignPreviewWindow.vue' // Gestionem ventanas const reload = ReloadRef(); @@ -24,6 +28,10 @@ const main_menu = windows.main_menu; const edit_profile = windows.edit_profile; const account_settings = windows.account_settings; const db_window = windows.db_window; +const campaign_list = windows.campaign_list; +const new_campaign = windows.new_campaign; +const join_campaign = windows.join_campaign; +const campaign_preview = windows.campaign_preview; @@ -36,6 +44,10 @@ const db_window = windows.db_window; + + + + diff --git a/client/src/views/partials/CampaignEntry.vue b/client/src/views/partials/CampaignEntry.vue new file mode 100644 index 00000000..dc33ad87 --- /dev/null +++ b/client/src/views/partials/CampaignEntry.vue @@ -0,0 +1,75 @@ + + + + + + diff --git a/client/src/views/partials/EditUserPartial.vue b/client/src/views/partials/EditUserPartial.vue index 707caf8b..861daae0 100644 --- a/client/src/views/partials/EditUserPartial.vue +++ b/client/src/views/partials/EditUserPartial.vue @@ -10,7 +10,7 @@ import Api from '@/services/Api' import url from '@/services/BackendURL' import useEmitter from '@/services/Emitter'; -import { ClearWindows, CreateWindow } from '../../services/Windows'; +import { ClearWindows, CreateWindow, CreateChildWindow } from '../../services/Windows'; const emitter = useEmitter(); const username = ref(""); @@ -26,30 +26,16 @@ function retrieveAvatar(){ function LogOut(){ LogoutUser(); - ClearWindows({type: "main_menu", title: "Dragonroll"}); - CreateWindow({ - type: "login", - id: "login", - title: "Login" - }); + ClearWindows({type: "main_menu"}); + CreateWindow('login'); } function EditProfile(){ - CreateWindow({ - type: "edit_profile", - id: "edit_profile", - title: "Edit Profile", - close: true - }); + CreateChildWindow('main_menu', 'edit_profile'); } function EditSettings(){ - CreateWindow({ - type: "account_settings", - id: "account_settings", - title: "Dragonroll settings", - close: true - }); + CreateChildWindow('main_menu', 'account_settings'); } onMounted(() => { @@ -92,9 +78,9 @@ onMounted(() => {
- - - + + +
diff --git a/client/src/views/partials/WindowHandle.vue b/client/src/views/partials/WindowHandle.vue index 3fe96664..3e8223f3 100644 --- a/client/src/views/partials/WindowHandle.vue +++ b/client/src/views/partials/WindowHandle.vue @@ -1,5 +1,5 @@ diff --git a/client/src/views/windows/LoginWindow.vue b/client/src/views/windows/LoginWindow.vue index 3b6ff225..eb6c73ed 100644 --- a/client/src/views/windows/LoginWindow.vue +++ b/client/src/views/windows/LoginWindow.vue @@ -32,7 +32,7 @@ onMounted(() => { successMessage.value = success; SetupHandle(id, handle); - SetSize(id, {x: 700, y: 630}); + SetSize(id, {x: 450, y: 480}); ResetPosition(id, "center"); }); @@ -62,20 +62,12 @@ function login(){ function ShowRegister(){ ClearWindows({type: "login"}); - CreateWindow({ - type: "register", - id: "register", - title: "Register" - }); + CreateWindow('register'); } function ShowMainMenu(){ ClearWindows({type: "login"}); - CreateWindow({ - type: "main_menu", - id: "main_menu", - title: "Dragonroll" - }); + CreateWindow('main_menu'); } @@ -97,7 +89,7 @@ function ShowMainMenu(){
- +
@@ -116,8 +108,6 @@ p { } .window-wrapper { - min-width: 700px; - min-height: 630px; user-select: none; display: flex; @@ -125,17 +115,22 @@ p { } .splash-image { - width: 600px; - height: 250px; + width: 400px; +} + +form { + padding-left: 30px; + padding-right: 30px; + width: 100%; } .form-field { - padding: 10px; - display: flex; - align-items: left; - flex-direction: column; - justify-content: left; - width: 600px; + width: 100%; + display: flex; + align-items: left; + flex-direction: column; + justify-content: left; + padding-top: 10px; } label { diff --git a/client/src/views/windows/MainMenuWindow.vue b/client/src/views/windows/MainMenuWindow.vue index 4e807942..ec5fed94 100644 --- a/client/src/views/windows/MainMenuWindow.vue +++ b/client/src/views/windows/MainMenuWindow.vue @@ -24,10 +24,12 @@ let title = data.title; onMounted(() => { SetupHandle(id, handle); - SetSize(id, {x: 500, y: 600}); + SetSize(id, {x: 500, y: 540}); ResetPosition(id, "center", emitter); }); +// ??? +/* function OpenDatabase(){ ClearWindow(id); CreateWindow({ @@ -44,13 +46,11 @@ function OpenDatabase(){ } }); } + */ -function OpenJoinCampaign(){ - ClearWindow(id); -} - -function OpenMyCampaigns(){ +function OpenCampaigns(){ ClearWindow(id); + CreateWindow('campaign_list'); } @@ -65,12 +65,11 @@ function OpenMyCampaigns(){

Main Menu

- - +
- - - + + +
diff --git a/client/src/views/windows/RegisterWindow.vue b/client/src/views/windows/RegisterWindow.vue index c963ce40..ca179529 100644 --- a/client/src/views/windows/RegisterWindow.vue +++ b/client/src/views/windows/RegisterWindow.vue @@ -29,7 +29,7 @@ let title = data.title; onMounted(() => { SetupHandle(id, handle); - SetSize(id, {x: 700, y: 630}); + SetSize(id, {x: 450, y: 780}); ResetPosition(id, "center"); }); @@ -65,12 +65,7 @@ function register(){ function ShowLogin(msg){ ClearWindows({type: "register"}); - CreateWindow({ - type: "login", - id: "login", - title: "Login", - success: msg - }); + CreateWindow('login', {success: msg}); } @@ -106,7 +101,7 @@ function ShowLogin(msg){
- +
@@ -126,17 +121,11 @@ p { } .window-wrapper { - min-width: 700px; - min-height: 630px; - display: flex; align-items: center; } .window-content { - overflow: auto; - min-width: 700px; - flex-direction: column; display: flex; align-items: center; @@ -144,8 +133,7 @@ p { } .splash-image { - width: 600px; - height: 250px; + width: 400px; user-select: none; } @@ -155,7 +143,10 @@ p { align-items: left; flex-direction: column; justify-content: left; - width: 600px; +} + +form { + width: 100%; } label { diff --git a/client/src/views/windows/campaigns/CampaignListWindow.vue b/client/src/views/windows/campaigns/CampaignListWindow.vue new file mode 100644 index 00000000..af6287ed --- /dev/null +++ b/client/src/views/windows/campaigns/CampaignListWindow.vue @@ -0,0 +1,135 @@ + + + + + + + \ No newline at end of file diff --git a/client/src/views/windows/campaigns/CampaignPreviewWindow.vue b/client/src/views/windows/campaigns/CampaignPreviewWindow.vue new file mode 100644 index 00000000..c9f10d38 --- /dev/null +++ b/client/src/views/windows/campaigns/CampaignPreviewWindow.vue @@ -0,0 +1,58 @@ + + + + + + + diff --git a/client/src/views/windows/campaigns/JoinCampaignWindow.vue b/client/src/views/windows/campaigns/JoinCampaignWindow.vue new file mode 100644 index 00000000..6b3b982d --- /dev/null +++ b/client/src/views/windows/campaigns/JoinCampaignWindow.vue @@ -0,0 +1,73 @@ + + + + + + + \ No newline at end of file diff --git a/client/src/views/windows/campaigns/NewCampaignWindow.vue b/client/src/views/windows/campaigns/NewCampaignWindow.vue new file mode 100644 index 00000000..3bd68a28 --- /dev/null +++ b/client/src/views/windows/campaigns/NewCampaignWindow.vue @@ -0,0 +1,84 @@ + + + + + + + \ No newline at end of file diff --git a/server/models/Campaign.js b/server/models/Campaign.js new file mode 100644 index 00000000..179feb26 --- /dev/null +++ b/server/models/Campaign.js @@ -0,0 +1,23 @@ +const mongoose = require("mongoose"); +const Schema = mongoose.Schema; + +const CampaignSchema = new Schema({ + name: {type: String, required: true}, + creation_date: { type: Date, default: Date.now}, + last_opened: { type: Date, default: Date.now}, + invite_code: { type: String, unique: true }, + image: { type: String } +}); + +CampaignSchema.statics.generateInvite = function() { + let possible = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTVWXYZ0123456789"; + let cod = ''; + + for (let i = 0; i < 32; i++) { + cod += possible.charAt(Math.floor(Math.random() * possible.length)); + } + + return cod; +} + +module.exports = mongoose.model('Campaign', CampaignSchema); \ No newline at end of file diff --git a/server/models/CampaignUser.js b/server/models/CampaignUser.js new file mode 100644 index 00000000..5d2319cb --- /dev/null +++ b/server/models/CampaignUser.js @@ -0,0 +1,10 @@ +const mongoose = require("mongoose") +const Schema = mongoose.Schema; + +const CampaignUserSchema = new Schema({ + user: {type: mongoose.Types.ObjectId, ref: "User"}, + campaign: {type: mongoose.Types.ObjectId, ref: "Campaign"}, + is_dm: {type: Boolean, default: false} +}); + +module.exports = mongoose.model('CampaignUser', CampaignUserSchema); \ No newline at end of file diff --git a/server/routes/campaign.js b/server/routes/campaign.js new file mode 100644 index 00000000..cc720497 --- /dev/null +++ b/server/routes/campaign.js @@ -0,0 +1,56 @@ +const express = require('express'); +const router = express.Router(); + +const passport = require('passport'); +const rateLimitMiddleware = require("../config/rate-limiter"); + +const Campaign = require("../models/Campaign"); +const CampaignUser = require("../models/CampaignUser"); + +const upload = require("../config/storage"); + +/* +router.post('/register', passport.authenticate('jwt', {session: false}), rateLimitMiddleware, (req, res) => { +}); +*/ + +router.post('/create', passport.authenticate('jwt', {session: false}), rateLimitMiddleware, (req, res) => { + let { + name + } = req.body; + + if(!(name)){ + res.json({ + status: "error", + msg: "params" + }); + return; + } + + // Create the campaign + let campaign = new Campaign({name}); + campaign.invite_code = Campaign.generateInvite(); + + campaign.save().then(campaign => { + // Create relation with our user and set him as dm + let campaignUser = new CampaignUser({ + user: req.user, + campaign, + is_dm: true + }); + + campaignUser.save().then(campaignUser => { + res.json(campaign); + return + }).catch((err) => {res.json({status: "error", msg: "internal"})}); + }).catch((err) => {res.json({status: "error", msg: "internal"})}); +}); + +router.get('/list', passport.authenticate('jwt', {session: false}), (req, res) => { + CampaignUser.find({user: req.user}).populate("campaign").then((data) => { + res.json(data); + return; + }).catch((err) => res.json({status: "error", msg: "internal"})); +}); + +module.exports = router; \ No newline at end of file diff --git a/server/server.js b/server/server.js index b53d74ec..2f09c920 100755 --- a/server/server.js +++ b/server/server.js @@ -54,6 +54,7 @@ app.use(cors()); // Routes (/ només) app.use('/user', require('./routes/user')); +app.use('/campaign', require('./routes/campaign')); // app.use('/users', require('./routes/users')); app.listen(PORT, () => {