Started inside ui
This commit is contained in:
parent
44771fc4af
commit
6f780382d0
7
client/package-lock.json
generated
7
client/package-lock.json
generated
@ -11,6 +11,7 @@
|
|||||||
"@kangc/v-md-editor": "^2.3.17",
|
"@kangc/v-md-editor": "^2.3.17",
|
||||||
"axios": "^1.5.1",
|
"axios": "^1.5.1",
|
||||||
"babel-plugin-prismjs": "^2.1.0",
|
"babel-plugin-prismjs": "^2.1.0",
|
||||||
|
"ef-infinite-canvas": "^0.6.6",
|
||||||
"form-data": "^4.0.0",
|
"form-data": "^4.0.0",
|
||||||
"jquery": "^3.7.1",
|
"jquery": "^3.7.1",
|
||||||
"jquery-ui": "^1.13.3",
|
"jquery-ui": "^1.13.3",
|
||||||
@ -5263,6 +5264,12 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/ef-infinite-canvas": {
|
||||||
|
"version": "0.6.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/ef-infinite-canvas/-/ef-infinite-canvas-0.6.6.tgz",
|
||||||
|
"integrity": "sha512-EB9hawOUrFJ0eJz3MSxRnLcTodoji99zZVQSdvCTHVq1/6LQF/BVI/L3K8+1VgPW/aTSOTJiOGzqrm+PpMx0EA==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/electron-to-chromium": {
|
"node_modules/electron-to-chromium": {
|
||||||
"version": "1.5.2",
|
"version": "1.5.2",
|
||||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.2.tgz",
|
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.2.tgz",
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
"@kangc/v-md-editor": "^2.3.17",
|
"@kangc/v-md-editor": "^2.3.17",
|
||||||
"axios": "^1.5.1",
|
"axios": "^1.5.1",
|
||||||
"babel-plugin-prismjs": "^2.1.0",
|
"babel-plugin-prismjs": "^2.1.0",
|
||||||
|
"ef-infinite-canvas": "^0.6.6",
|
||||||
"form-data": "^4.0.0",
|
"form-data": "^4.0.0",
|
||||||
"jquery": "^3.7.1",
|
"jquery": "^3.7.1",
|
||||||
"jquery-ui": "^1.13.3",
|
"jquery-ui": "^1.13.3",
|
||||||
|
@ -59,6 +59,8 @@
|
|||||||
--color-background-softest: var(--c-white-softest);
|
--color-background-softest: var(--c-white-softest);
|
||||||
--color-button-active: var(--c-white-muter);
|
--color-button-active: var(--c-white-muter);
|
||||||
|
|
||||||
|
--text-disabled: #7e7e7e;
|
||||||
|
|
||||||
--color-scrollbar: var(--c-white-muter);
|
--color-scrollbar: var(--c-white-muter);
|
||||||
|
|
||||||
--color-hover: var(--c-white-mute);
|
--color-hover: var(--c-white-mute);
|
||||||
@ -92,6 +94,7 @@
|
|||||||
--separator: var(--c-white-mute);
|
--separator: var(--c-white-mute);
|
||||||
|
|
||||||
--chat-background: var(--c-blacker);
|
--chat-background: var(--c-blacker);
|
||||||
|
--text-disabled: #7e7e7e;
|
||||||
|
|
||||||
--color-hover: var()
|
--color-hover: var()
|
||||||
}
|
}
|
||||||
|
@ -60,6 +60,13 @@ a {
|
|||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.btn-green {
|
||||||
|
background-color: var(--c-button-green);
|
||||||
|
}
|
||||||
|
.btn-green:hover {
|
||||||
|
background-color: var(--c-button-green-hover);
|
||||||
|
}
|
||||||
|
|
||||||
hr {
|
hr {
|
||||||
border: 0;
|
border: 0;
|
||||||
height: 1px;
|
height: 1px;
|
||||||
|
@ -35,6 +35,7 @@ VueMarkdownEditor.use(createKatexPlugin());
|
|||||||
const app = createApp(App).use(VueMarkdownEditor);
|
const app = createApp(App).use(VueMarkdownEditor);
|
||||||
|
|
||||||
|
|
||||||
|
app.config.globalProperties.emitter = emitter
|
||||||
app.config.globalProperties.rollWindows = {
|
app.config.globalProperties.rollWindows = {
|
||||||
login: reactive([]),
|
login: reactive([]),
|
||||||
register: reactive([]),
|
register: reactive([]),
|
||||||
|
@ -5,17 +5,31 @@ import Api from '@/services/Api'
|
|||||||
import { backendUrl } from './BackendURL';
|
import { backendUrl } from './BackendURL';
|
||||||
import { GetUser } from './User';
|
import { GetUser } from './User';
|
||||||
|
|
||||||
|
let emitter;
|
||||||
|
|
||||||
|
function SetEmitter(newEmitter){
|
||||||
|
emitter = newEmitter
|
||||||
|
}
|
||||||
|
|
||||||
|
function DisplayToast(color, text, duration = 1000){
|
||||||
|
emitter.emit("toast", {color, text, duration});
|
||||||
|
}
|
||||||
|
|
||||||
export const socket = io(backendUrl)
|
export const socket = io(backendUrl)
|
||||||
|
|
||||||
let currentCampaign = null;
|
let currentCampaign = null;
|
||||||
|
let currentPlayer = null;
|
||||||
|
|
||||||
const players = ref([]);
|
const players = ref([]);
|
||||||
let GetPlayerList = () => { return players; };
|
let GetPlayerList = () => { return players; };
|
||||||
|
let GetCampaign = () => { return currentCampaign; };
|
||||||
|
let GetClient = () => { return currentPlayer; };
|
||||||
|
|
||||||
socket.on('update-players', data => {
|
socket.on('update-players', data => {
|
||||||
players.value = [];
|
players.value = [];
|
||||||
Object.keys(data).forEach((key) => {
|
Object.keys(data).forEach((key) => {
|
||||||
players.value.push(data[key]);
|
players.value.push(data[key]);
|
||||||
|
if(GetUser()._id == data[key].user._id) currentPlayer = data[key];
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -34,10 +48,22 @@ function Disconnect(){
|
|||||||
currentCampaign = null;
|
currentCampaign = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function GetPlayer(player_campaign){
|
||||||
|
let index = players.value.findIndex((p) => {return p._id == player_campaign});
|
||||||
|
if(index != -1) return players.value[index];
|
||||||
|
}
|
||||||
|
|
||||||
export {
|
export {
|
||||||
|
SetEmitter,
|
||||||
|
|
||||||
|
DisplayToast,
|
||||||
|
|
||||||
DisplayCampaign,
|
DisplayCampaign,
|
||||||
ConnectToCampaign,
|
ConnectToCampaign,
|
||||||
Disconnect,
|
Disconnect,
|
||||||
|
|
||||||
|
GetCampaign,
|
||||||
|
GetClient,
|
||||||
GetPlayerList,
|
GetPlayerList,
|
||||||
|
GetPlayer,
|
||||||
};
|
};
|
20
client/src/services/Game.js
Normal file
20
client/src/services/Game.js
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
import { ref } from "vue";
|
||||||
|
|
||||||
|
const inGameRef = ref(false);
|
||||||
|
let InGameRef = () => inGameRef;
|
||||||
|
|
||||||
|
function LaunchGame(){
|
||||||
|
inGameRef.value = true;
|
||||||
|
console.log("jdksadjlo")
|
||||||
|
}
|
||||||
|
|
||||||
|
function ExitGame(){
|
||||||
|
inGameRef.value = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
export {
|
||||||
|
LaunchGame,
|
||||||
|
ExitGame,
|
||||||
|
|
||||||
|
InGameRef
|
||||||
|
};
|
@ -2,14 +2,22 @@
|
|||||||
import { onMounted } from 'vue';
|
import { onMounted } from 'vue';
|
||||||
import { RouterLink, RouterView } from 'vue-router'
|
import { RouterLink, RouterView } from 'vue-router'
|
||||||
|
|
||||||
|
import useEmitter from '@/services/Emitter';
|
||||||
|
const emitter = useEmitter();
|
||||||
|
|
||||||
import WindowManager from '@/views/managers/WindowManager.vue'
|
import WindowManager from '@/views/managers/WindowManager.vue'
|
||||||
import { GetUser } from '@/services/User'
|
import { GetUser } from '@/services/User'
|
||||||
|
|
||||||
import { CreateWindow } from '@/services/Windows'
|
import { CreateWindow } from '@/services/Windows'
|
||||||
|
import Toast from './partials/Toast.vue';
|
||||||
|
import { DisplayToast, SetEmitter } from '../services/Dragonroll';
|
||||||
|
import GameManager from './managers/GameManager.vue';
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
|
SetEmitter(emitter);
|
||||||
if(GetUser()){
|
if(GetUser()){
|
||||||
CreateWindow('main_menu')
|
CreateWindow('main_menu')
|
||||||
|
DisplayToast('green', 'Logged in successfully as ' + GetUser().username + '!', 3000)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
CreateWindow('login');
|
CreateWindow('login');
|
||||||
@ -20,6 +28,8 @@ onMounted(() => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
<Toast></Toast>
|
||||||
|
<GameManager></GameManager>
|
||||||
<WindowManager></WindowManager>
|
<WindowManager></WindowManager>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
39
client/src/views/managers/GameManager.vue
Normal file
39
client/src/views/managers/GameManager.vue
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
<script setup>
|
||||||
|
import { onMounted, ref, watch } from 'vue';
|
||||||
|
import { InGameRef } from '../../services/Game';
|
||||||
|
import IconButton from '../partials/game/IconButton.vue';
|
||||||
|
import { AddSound } from '../../services/Sound';
|
||||||
|
|
||||||
|
const game = ref(null);
|
||||||
|
const in_game = InGameRef();
|
||||||
|
|
||||||
|
watch(game, () => {
|
||||||
|
if(game) AddSound(game.value);
|
||||||
|
})
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="game-root" ref="game" v-if="in_game">
|
||||||
|
<div class="vertical-button">
|
||||||
|
<IconButton icon="icons/iconoir/regular/menu.svg"></IconButton>
|
||||||
|
<IconButton icon="icons/iconoir/regular/cursor-pointer.svg"></IconButton>
|
||||||
|
<IconButton icon="icons/game-icons/000000/delapouite/rolling-dice-cup.svg"></IconButton>
|
||||||
|
</div>
|
||||||
|
<div class="horizontal-button">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.vertical-button {
|
||||||
|
position: absolute;
|
||||||
|
top: 10px;
|
||||||
|
left: 10px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
</style>
|
@ -2,38 +2,60 @@
|
|||||||
import { onMounted, onUpdated, ref } from 'vue';
|
import { onMounted, onUpdated, ref } from 'vue';
|
||||||
import Api from '@/services/Api.js'
|
import Api from '@/services/Api.js'
|
||||||
import { backendUrl } from '../../services/BackendURL';
|
import { backendUrl } from '../../services/BackendURL';
|
||||||
|
import { GetClient, GetPlayer, GetPlayerList } from '../../services/Dragonroll';
|
||||||
|
import { GetUser } from '../../services/User';
|
||||||
|
|
||||||
const props = defineProps(['player']);
|
const props = defineProps(['player']);
|
||||||
const player = props.player;
|
let player = props.player;
|
||||||
|
|
||||||
const playerName = ref("");
|
const playerName = ref("");
|
||||||
const isDm = ref(false);
|
const isDm = ref(false);
|
||||||
|
|
||||||
const avatar = ref(null);
|
const avatar = ref(null);
|
||||||
|
const container = ref(null);
|
||||||
|
const status = ref("");
|
||||||
|
|
||||||
function retrieveAvatar(){
|
function retrieveAvatar(){
|
||||||
let userAvatarDisplay = avatar.value;
|
let userAvatarDisplay = avatar.value;
|
||||||
Api().get('/user/retrieve-avatar?username=' + player.data.username).then((response) => {
|
Api().get('/user/retrieve-avatar?username=' + player.user.username).then((response) => {
|
||||||
if(response.data.image) userAvatarDisplay.src = backendUrl + "public/" + response.data.image;
|
if(response.data.image) userAvatarDisplay.src = backendUrl + "public/" + response.data.image;
|
||||||
}).catch((err) => console.log("Internal error"));
|
}).catch((err) => console.log("Internal error"));
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
playerName.value = player.data.username;
|
playerName.value = player.user.username;
|
||||||
if(player.is_dm) isDm.value = true;
|
if(player.is_dm) isDm.value = true;
|
||||||
|
if(player.online) status.value = "online";
|
||||||
|
else status.value = "";
|
||||||
|
|
||||||
retrieveAvatar();
|
retrieveAvatar();
|
||||||
|
|
||||||
|
if(player.user._id == GetUser()._id){
|
||||||
|
container.value.classList.add("current-player");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
onUpdated(() => {
|
||||||
|
player = GetPlayer(player._id);
|
||||||
|
playerName.value = player.user.username;
|
||||||
|
if(player.is_dm) isDm.value = true;
|
||||||
|
if(player.online) status.value = "online";
|
||||||
|
else status.value = "";
|
||||||
});
|
});
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="main-player-container">
|
<div class="main-player-container" ref="container">
|
||||||
<div class="main-player-container-inner">
|
<div class="main-player-container-inner">
|
||||||
<img class="user-icon" src="img/def-avatar.jpg" ref="avatar" draggable="false">
|
<img class="user-icon" src="img/def-avatar.jpg" ref="avatar" draggable="false">
|
||||||
<div class="main-user-info">
|
<div class="main-user-info">
|
||||||
<b>{{ playerName }}</b><span class="dm" v-if="isDm"> DM</span><br>Miauler
|
<b>{{ playerName }}</b><span class="dm" v-if="isDm"> DM</span>
|
||||||
|
<br>
|
||||||
|
<span class="offline-indicator" v-if="status == ''">Offline</span>
|
||||||
|
<span class="online-indicator" v-if="status == 'online'">Online</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="main-user-actions">
|
<div class="main-user-actions">
|
||||||
@ -45,6 +67,38 @@ onMounted(() => {
|
|||||||
|
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
.current-player {
|
||||||
|
background-color: var(--color-background-softer);
|
||||||
|
}
|
||||||
|
|
||||||
|
.offline-indicator {
|
||||||
|
&:before {
|
||||||
|
content: '';
|
||||||
|
display: inline-block;
|
||||||
|
width: 9px;
|
||||||
|
height: 9px;
|
||||||
|
background-color: var(--text-disabled);
|
||||||
|
border-radius: 8px;
|
||||||
|
margin-bottom: 1px;
|
||||||
|
margin-right: 5px;
|
||||||
|
}
|
||||||
|
color: var(--text-disabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
.online-indicator {
|
||||||
|
&:before {
|
||||||
|
content: '';
|
||||||
|
display: inline-block;
|
||||||
|
width: 9px;
|
||||||
|
height: 9px;
|
||||||
|
background-color: var(--c-text-green);
|
||||||
|
border-radius: 8px;
|
||||||
|
margin-bottom: 1px;
|
||||||
|
margin-right: 5px;
|
||||||
|
}
|
||||||
|
color: var(--c-text-green);
|
||||||
|
}
|
||||||
|
|
||||||
#send-avatar-form {
|
#send-avatar-form {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
@ -14,11 +14,14 @@ const players = GetPlayerList();
|
|||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<PlayerEntry v-for="player in players" :key="player.data._id" :player="player" class="player-list"></PlayerEntry>
|
<div class="player-list">
|
||||||
|
<PlayerEntry v-for="player in players" :key="player._id" :player="player"></PlayerEntry>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
||||||
|
104
client/src/views/partials/Toast.vue
Normal file
104
client/src/views/partials/Toast.vue
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
<script setup>
|
||||||
|
import { ref } from 'vue';
|
||||||
|
|
||||||
|
import useEmitter from '@/services/Emitter';
|
||||||
|
|
||||||
|
const emitter = useEmitter();
|
||||||
|
|
||||||
|
const text = ref("");
|
||||||
|
const toast = ref(null);
|
||||||
|
|
||||||
|
emitter.on('toast', data => {
|
||||||
|
text.value = data.text;
|
||||||
|
|
||||||
|
toast.value.classList.add(data.color);
|
||||||
|
toast.value.classList.add("show");
|
||||||
|
setTimeout(() => {
|
||||||
|
toast.value.classList.add("sliding");
|
||||||
|
setTimeout(() => {
|
||||||
|
toast.value.style = {};
|
||||||
|
toast.value.classList.remove("show");
|
||||||
|
toast.value.classList.remove("sliding");
|
||||||
|
toast.value.classList.remove(data.color);
|
||||||
|
}, 400);
|
||||||
|
}, data.duration);
|
||||||
|
});
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="toast" ref="toast">
|
||||||
|
<div class="toast-container">{{ text }}</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.toast-container {
|
||||||
|
height: 100%;
|
||||||
|
background-color: var(--color-background-soft);
|
||||||
|
padding: 10px;
|
||||||
|
margin-left: 5px;
|
||||||
|
border-top-right-radius: 6px;
|
||||||
|
border-bottom-right-radius: 6px;
|
||||||
|
transform: translate(2px,0px)
|
||||||
|
}
|
||||||
|
|
||||||
|
.toast {
|
||||||
|
position: absolute;
|
||||||
|
display: none;
|
||||||
|
|
||||||
|
top: 10px;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, 0);
|
||||||
|
|
||||||
|
min-width: 400px;
|
||||||
|
min-height: 40px;
|
||||||
|
border-radius: 6px;
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
|
||||||
|
animation: slide-in 0.4s ease-in-out;
|
||||||
|
@keyframes slide-in {
|
||||||
|
0% {
|
||||||
|
transform: translate(-50%,-50px);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.sliding {
|
||||||
|
@keyframes slide-out {
|
||||||
|
0% {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: translate(-50%,-50px);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
animation: slide-out .4s ease-in-out forwards;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.show {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Colors!!!! */
|
||||||
|
|
||||||
|
&.red {
|
||||||
|
background-color: rgb(243, 68, 68);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.green {
|
||||||
|
background-color: rgb(92, 199, 92);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.aqua {
|
||||||
|
background-color: rgb(113, 250, 250);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
21
client/src/views/partials/books/BookItem.vue
Normal file
21
client/src/views/partials/books/BookItem.vue
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
<script setup>
|
||||||
|
import { onMounted, onUpdated, ref } from 'vue';
|
||||||
|
|
||||||
|
const props = defineProps(['book']);
|
||||||
|
const book = props.book;
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="book-item">A</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
|
||||||
|
</style>
|
34
client/src/views/partials/books/CampaignBookList.vue
Normal file
34
client/src/views/partials/books/CampaignBookList.vue
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
<script setup>
|
||||||
|
import { onMounted, onUpdated, ref } from 'vue';
|
||||||
|
import { SetupHandle, SetSize, SetPosition, ResetPosition } from '@/services/Windows';
|
||||||
|
|
||||||
|
import BookItem from './BookItem.vue';
|
||||||
|
|
||||||
|
/*
|
||||||
|
const props = defineProps(['campaign']);
|
||||||
|
const players = GetPlayerList();
|
||||||
|
*/
|
||||||
|
|
||||||
|
const props = defineProps(['books']);
|
||||||
|
const books = props.books;
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="book-list">
|
||||||
|
<BookItem v-for="book in books" :key="book._id" :book="book"></BookItem>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.book-list {
|
||||||
|
height: 100%;
|
||||||
|
background-color: var(--color-background);
|
||||||
|
}
|
||||||
|
</style>
|
37
client/src/views/partials/game/IconButton.vue
Normal file
37
client/src/views/partials/game/IconButton.vue
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
<script setup>
|
||||||
|
const props = defineProps(['icon']);
|
||||||
|
let icon = props.icon;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="icon-button sound-click">
|
||||||
|
<img class="icon" :src="icon">
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.icon-button {
|
||||||
|
height: 32px;
|
||||||
|
width: 32px;
|
||||||
|
background-color: var(--color-background-soft);
|
||||||
|
border-radius: 6px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
margin: 2px;
|
||||||
|
|
||||||
|
transition: .3s background-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-button:hover {
|
||||||
|
background-color: var(--color-background-softer);
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
height: 24px;
|
||||||
|
width: 24px;
|
||||||
|
}
|
||||||
|
</style>
|
@ -6,12 +6,13 @@ import SuccessMessage from '@/views/others/SuccessMessage.vue'
|
|||||||
|
|
||||||
import { onMounted, onUpdated, ref } from 'vue';
|
import { onMounted, onUpdated, ref } from 'vue';
|
||||||
import { SetupHandle, SetSize, SetPosition, ResetPosition } from '@/services/Windows';
|
import { SetupHandle, SetSize, SetPosition, ResetPosition } from '@/services/Windows';
|
||||||
import { SetUser } from '@/services/User'
|
import { SetUser, GetUser } from '@/services/User'
|
||||||
|
|
||||||
import Api from '@/services/Api.js'
|
import Api from '@/services/Api.js'
|
||||||
|
|
||||||
import WindowHandle from '@/views/partials/WindowHandle.vue';
|
import WindowHandle from '@/views/partials/WindowHandle.vue';
|
||||||
import { ClearWindows, CreateWindow } from '../../services/Windows';
|
import { ClearWindows, CreateWindow } from '../../services/Windows';
|
||||||
|
import { DisplayToast } from '../../services/Dragonroll';
|
||||||
|
|
||||||
const handle = ref(null);
|
const handle = ref(null);
|
||||||
|
|
||||||
@ -46,7 +47,6 @@ function login(){
|
|||||||
errorMessage.value = "";
|
errorMessage.value = "";
|
||||||
SetUser(data.token);
|
SetUser(data.token);
|
||||||
|
|
||||||
console.log("Logged successfully");
|
|
||||||
ShowMainMenu();
|
ShowMainMenu();
|
||||||
}
|
}
|
||||||
}).catch((error) => {
|
}).catch((error) => {
|
||||||
@ -68,6 +68,7 @@ function ShowRegister(){
|
|||||||
function ShowMainMenu(){
|
function ShowMainMenu(){
|
||||||
ClearWindows({type: "login"});
|
ClearWindows({type: "login"});
|
||||||
CreateWindow('main_menu');
|
CreateWindow('main_menu');
|
||||||
|
DisplayToast('green', 'Logged in successfully as ' + GetUser().username + '!', 3000)
|
||||||
}
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
@ -10,6 +10,7 @@ import { SetupHandle, SetSize, SetPosition, ResetPosition } from '@/services/Win
|
|||||||
import Api from '@/services/Api.js'
|
import Api from '@/services/Api.js'
|
||||||
|
|
||||||
import { ClearWindows, CreateWindow } from '../../services/Windows';
|
import { ClearWindows, CreateWindow } from '../../services/Windows';
|
||||||
|
import { DisplayToast } from '../../services/Dragonroll';
|
||||||
|
|
||||||
const email = ref("");
|
const email = ref("");
|
||||||
const name = ref("");
|
const name = ref("");
|
||||||
@ -56,7 +57,7 @@ function register(){
|
|||||||
} else {
|
} else {
|
||||||
errorMessage.value = "";
|
errorMessage.value = "";
|
||||||
console.log("Logged successfully");
|
console.log("Logged successfully");
|
||||||
ShowLogin("Account created successfully");
|
DisplayToast('green', 'Account created successfully, now log in!', 3000);
|
||||||
}
|
}
|
||||||
}).catch((error) => {
|
}).catch((error) => {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
|
@ -66,7 +66,7 @@ function RefreshCampaigns(){
|
|||||||
<div class="window-second-header">
|
<div class="window-second-header">
|
||||||
<h2>Other campaigns</h2>
|
<h2>Other campaigns</h2>
|
||||||
<div class="campaign-list">
|
<div class="campaign-list">
|
||||||
|
<CampaignEntry v-for="camp in otherCampaigns" :key="camp._id" :data="camp"></CampaignEntry>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
@ -4,6 +4,10 @@ import { SetupHandle, SetSize, SetPosition, ResetPosition } from '@/services/Win
|
|||||||
|
|
||||||
import WindowHandle from '@/views/partials/WindowHandle.vue';
|
import WindowHandle from '@/views/partials/WindowHandle.vue';
|
||||||
import PlayerList from '../../partials/PlayerList.vue';
|
import PlayerList from '../../partials/PlayerList.vue';
|
||||||
|
import { DisplayToast, GetCampaign, GetClient } from '../../../services/Dragonroll';
|
||||||
|
import CampaignBookList from '../../partials/books/CampaignBookList.vue';
|
||||||
|
import { ClearWindow } from '../../../services/Windows';
|
||||||
|
import { LaunchGame } from '../../../services/Game';
|
||||||
|
|
||||||
const handle = ref(null);
|
const handle = ref(null);
|
||||||
|
|
||||||
@ -15,7 +19,20 @@ onMounted(() => {
|
|||||||
SetupHandle(id, handle);
|
SetupHandle(id, handle);
|
||||||
SetSize(id, {x: 1200, y: 750});
|
SetSize(id, {x: 1200, y: 750});
|
||||||
ResetPosition(id, "center");
|
ResetPosition(id, "center");
|
||||||
|
|
||||||
|
console.log(data);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function CopyCode(){
|
||||||
|
navigator.clipboard.writeText(GetCampaign().invite_code);
|
||||||
|
DisplayToast('aqua', "Copied invite code successfully!", 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
function Launch(){
|
||||||
|
ClearWindow('campaign_preview');
|
||||||
|
LaunchGame();
|
||||||
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
||||||
@ -30,8 +47,22 @@ onMounted(() => {
|
|||||||
<div class="campaign-preview-column left">
|
<div class="campaign-preview-column left">
|
||||||
<h2>Players</h2>
|
<h2>Players</h2>
|
||||||
<PlayerList :campaign="data.campaign"></PlayerList>
|
<PlayerList :campaign="data.campaign"></PlayerList>
|
||||||
|
<div class="buttons-row">
|
||||||
|
<button class="btn-primary button-row sound-click" v-on:click.prevent="CopyCode">Copy invite code</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="campaign-preview-column center">
|
<div class="campaign-preview-column center">
|
||||||
|
<h1 class="campaign-title">{{ data.campaign.name }}</h1>
|
||||||
|
<div class="campaign-main-container">
|
||||||
|
<div class="campaign-main-container-scroll">
|
||||||
|
<div class="">Dnd 5e</div>
|
||||||
|
<h2>Books</h2>
|
||||||
|
<CampaignBookList class="small-book-list"></CampaignBookList>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="buttons-row">
|
||||||
|
<button class="btn-primary button-row sound-click btn-green" v-on:click.prevent="Launch">Launch game</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="campaign-preview-column right">
|
<div class="campaign-preview-column right">
|
||||||
<h2>Chat</h2>
|
<h2>Chat</h2>
|
||||||
@ -43,12 +74,47 @@ onMounted(() => {
|
|||||||
|
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
.small-book-list {
|
||||||
|
height: 400px;
|
||||||
|
margin: 20px;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.campaign-main-container-scroll {
|
||||||
|
overflow-y: scroll;
|
||||||
|
height: 100%;
|
||||||
|
max-height: 520px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.campaign-main-container {
|
||||||
|
height: 100%;
|
||||||
|
h2 {
|
||||||
|
text-align: left;
|
||||||
|
margin-left: 20px;
|
||||||
|
font-size: 28px;
|
||||||
|
}
|
||||||
|
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.campaign-title {
|
||||||
|
font-weight: normal;
|
||||||
|
text-align: left;
|
||||||
|
padding: 20px;
|
||||||
|
margin-bottom: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button-row {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
.campaign-preview-container {
|
.campaign-preview-container {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 3fr 5fr 5fr;
|
grid-template-columns: 3fr 5fr 4fr;
|
||||||
}
|
}
|
||||||
|
|
||||||
.campaign-preview-column {
|
.campaign-preview-column {
|
||||||
@ -61,6 +127,8 @@ onMounted(() => {
|
|||||||
|
|
||||||
&.center {
|
&.center {
|
||||||
background-color: var(--color-background-semisoft);
|
background-color: var(--color-background-semisoft);
|
||||||
|
|
||||||
|
display: flex;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.right {
|
&.right {
|
||||||
|
@ -3,6 +3,9 @@ import { onMounted, onUpdated, ref } from 'vue';
|
|||||||
import { SetupHandle, SetSize, SetPosition, ResetPosition } from '@/services/Windows';
|
import { SetupHandle, SetSize, SetPosition, ResetPosition } from '@/services/Windows';
|
||||||
|
|
||||||
import WindowHandle from '@/views/partials/WindowHandle.vue';
|
import WindowHandle from '@/views/partials/WindowHandle.vue';
|
||||||
|
import { DisplayToast } from '../../../services/Dragonroll';
|
||||||
|
import { ClearWindow } from '../../../services/Windows';
|
||||||
|
import Api from '@/services/Api'
|
||||||
|
|
||||||
const handle = ref(null);
|
const handle = ref(null);
|
||||||
|
|
||||||
@ -17,6 +20,26 @@ onMounted(() => {
|
|||||||
SetSize(id, {x: 300, y: 150});
|
SetSize(id, {x: 300, y: 150});
|
||||||
ResetPosition(id, "center");
|
ResetPosition(id, "center");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function JoinCampaign(){
|
||||||
|
let invite_code = code.value;
|
||||||
|
Api().post('/campaign/join', {
|
||||||
|
invite_code
|
||||||
|
}).then(response => {
|
||||||
|
if(response.data.status == "ok"){
|
||||||
|
DisplayToast('green', "Successfully joined the campaign!", 2000);
|
||||||
|
let campaign = response.data.campaign;
|
||||||
|
|
||||||
|
ConnectToCampaign(campaign);
|
||||||
|
DisplayCampaign(campaign);
|
||||||
|
} else if(response.data.msg == "already"){
|
||||||
|
DisplayToast('red', "You are already in that campaign!", 2000);
|
||||||
|
} else {
|
||||||
|
DisplayToast('red', "Error joining this campaign (maybe the code is not valid?)", 2000);
|
||||||
|
}
|
||||||
|
}).catch((err) => console.log(err));
|
||||||
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
||||||
@ -30,7 +53,7 @@ onMounted(() => {
|
|||||||
<input id="username-field" type="text" placeholder="Enter campaign code..." name="code" v-model="code" autocomplete="off" >
|
<input id="username-field" type="text" placeholder="Enter campaign code..." name="code" v-model="code" autocomplete="off" >
|
||||||
</div>
|
</div>
|
||||||
<div class="form-field">
|
<div class="form-field">
|
||||||
<button class="btn-primary sound-click">Join</button>
|
<button class="btn-primary sound-click" v-on:click.prevent="JoinCampaign">Join</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
@ -4,31 +4,44 @@ const FilterUser = require('../utils/filters');
|
|||||||
|
|
||||||
let sessions = {};
|
let sessions = {};
|
||||||
|
|
||||||
|
async function GetOfflinePlayers(campaign){
|
||||||
|
let players = await CampaignUser.find({campaign}).populate('user').exec();
|
||||||
|
let finalPlayers = [];
|
||||||
|
console.log(players)
|
||||||
|
// TODO: Filter
|
||||||
|
players.forEach(player => finalPlayers.push(FilterUser(player)));
|
||||||
|
|
||||||
|
return finalPlayers;
|
||||||
|
}
|
||||||
|
|
||||||
|
function SetPlayerProperty(campaign, player_id, key, value){
|
||||||
|
objIndex = sessions[campaign].players.findIndex(player => player.user._id.toString() == player_id);
|
||||||
|
if(objIndex != -1){
|
||||||
|
sessions[campaign].players[objIndex][key] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = io => {
|
module.exports = io => {
|
||||||
io.on('connection', (socket) => {
|
io.on('connection', (socket) => {
|
||||||
socket.on('enter', (user, campaignId) => {
|
socket.on('enter', (user, campaignId) => {
|
||||||
User.findOne(user).then(user => {
|
User.findOne(user).then(user => {
|
||||||
if(user){
|
if(user){
|
||||||
socket.user = user;
|
socket.user = user;
|
||||||
CampaignUser.findOne({campaign: campaignId, user}).then(campaignUser => {
|
CampaignUser.findOne({campaign: campaignId, user}).then(async campaignUser => {
|
||||||
if(campaignUser){
|
if(campaignUser){
|
||||||
socket.join(campaignId);
|
socket.join(campaignId);
|
||||||
socket.campaign = campaignId;
|
socket.campaign = campaignId;
|
||||||
|
|
||||||
if(!sessions[campaignId]) sessions[campaignId] = {
|
if(!sessions[campaignId]) sessions[campaignId] = {
|
||||||
players: {}
|
players: await GetOfflinePlayers(campaignId)
|
||||||
};
|
};
|
||||||
|
|
||||||
sessions[campaignId].players[socket.user._id] = {
|
|
||||||
online: true,
|
|
||||||
is_dm: campaignUser.is_dm,
|
|
||||||
data: FilterUser(socket.user)
|
|
||||||
};
|
|
||||||
|
|
||||||
console.log(socket.user.username + " ha entrado!");
|
console.log(socket.user.username + " ha entrado!");
|
||||||
|
SetPlayerProperty(campaignId, socket.user._id, "online", true);
|
||||||
io.to(socket.campaign).emit('update-players', sessions[campaignId].players)
|
io.to(socket.campaign).emit('update-players', sessions[campaignId].players)
|
||||||
|
|
||||||
console.log(JSON.stringify(sessions, null, 4));
|
// console.log(JSON.stringify(sessions[campaignId], null, 4));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -36,9 +49,9 @@ module.exports = io => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
socket.on('exit', () => {
|
socket.on('exit', () => {
|
||||||
io.to(socket.campaign).emit('update-players', sessions[campaignId].players)
|
SetPlayerProperty(socket.campaign, socket.user._id, "online", false);
|
||||||
|
io.to(socket.campaign).emit('update-players', sessions[socket.campaign].players)
|
||||||
socket.leave(socket.campaign)
|
socket.leave(socket.campaign)
|
||||||
sessions[socket.campaign].players[socket.user._id].online = false;
|
|
||||||
|
|
||||||
console.log(socket.user.username + " ha salido!")
|
console.log(socket.user.username + " ha salido!")
|
||||||
});
|
});
|
||||||
|
@ -46,9 +46,45 @@ router.post('/create', passport.authenticate('jwt', {session: false}), rateLimit
|
|||||||
}).catch((err) => {res.json({status: "error", msg: "internal"})});
|
}).catch((err) => {res.json({status: "error", msg: "internal"})});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
router.post('/join', passport.authenticate('jwt', {session: false}), rateLimitMiddleware, (req, res) => {
|
||||||
|
let {
|
||||||
|
invite_code
|
||||||
|
} = req.body;
|
||||||
|
|
||||||
|
if(!(invite_code)){
|
||||||
|
res.json({
|
||||||
|
status: "error",
|
||||||
|
msg: "params"
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Campaign.findOne({invite_code}).then(campaign => {
|
||||||
|
if(campaign){
|
||||||
|
let campaignUser = new CampaignUser({
|
||||||
|
user: req.user,
|
||||||
|
campaign,
|
||||||
|
is_dm: false
|
||||||
|
});
|
||||||
|
|
||||||
|
campaignUser.save().then(campaignUser => {
|
||||||
|
res.json({status: "ok", campaign});
|
||||||
|
return;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
res.json({
|
||||||
|
status: "error",
|
||||||
|
msg: "not valid"
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}).catch(err => res.json({status: "error", msg: "internal"}))
|
||||||
|
});
|
||||||
|
|
||||||
router.get('/list', passport.authenticate('jwt', {session: false}), (req, res) => {
|
router.get('/list', passport.authenticate('jwt', {session: false}), (req, res) => {
|
||||||
CampaignUser.find({user: req.user}).populate("campaign").then((data) => {
|
CampaignUser.find({user: req.user}).populate("campaign").then((data) => {
|
||||||
res.json(data);
|
res.json(data);
|
||||||
|
console.log(data);
|
||||||
return;
|
return;
|
||||||
}).catch((err) => res.json({status: "error", msg: "internal"}));
|
}).catch((err) => res.json({status: "error", msg: "internal"}));
|
||||||
});
|
});
|
||||||
@ -56,6 +92,8 @@ router.get('/list', passport.authenticate('jwt', {session: false}), (req, res) =
|
|||||||
router.get('/players', passport.authenticate('jwt', {session: false}), (req, res) => {
|
router.get('/players', passport.authenticate('jwt', {session: false}), (req, res) => {
|
||||||
Campaign.findById(req.query.campaign).then((campaign) => {
|
Campaign.findById(req.query.campaign).then((campaign) => {
|
||||||
CampaignUser.find({campaign}).populate('user').then((data) => {
|
CampaignUser.find({campaign}).populate('user').then((data) => {
|
||||||
|
console.log("djskajdk")
|
||||||
|
console.log(data);
|
||||||
res.json(data);
|
res.json(data);
|
||||||
return;
|
return;
|
||||||
}).catch((err) => res.json({status: "error", msg: "internal"}));
|
}).catch((err) => res.json({status: "error", msg: "internal"}));
|
||||||
|
Loading…
Reference in New Issue
Block a user