Compare commits

..

3 Commits

Author SHA1 Message Date
Stefan Zermatten
21b823f85c Dark mode now with 20% more dark 2020-05-31 22:25:04 +02:00
Stefan Zermatten
4631579181 Character toolbar now correctly uses dark and light text where appropriate 2020-05-31 22:22:42 +02:00
Stefan Zermatten
edf3920e84 Character sheet toolbars now match the color of the character 2020-05-31 22:16:38 +02:00
8 changed files with 302 additions and 37 deletions

View File

@@ -143,7 +143,15 @@ const updateCreature = new ValidatedMethod({
validate({_id, path}){ validate({_id, path}){
if (!_id) return false; if (!_id) return false;
// Allowed fields // Allowed fields
let allowedFields = ['name', 'alignment', 'gender', 'picture', 'avatarPicture', 'settings']; let allowedFields = [
'name',
'alignment',
'gender',
'picture',
'avatarPicture',
'color',
'settings',
];
if (!allowedFields.includes(path[0])){ if (!allowedFields.includes(path[0])){
throw new Meteor.Error('Creatures.methods.update.denied', throw new Meteor.Error('Creatures.methods.update.denied',
'This field can\'t be updated using this method'); 'This field can\'t be updated using this method');
@@ -152,9 +160,15 @@ const updateCreature = new ValidatedMethod({
run({_id, path, value}) { run({_id, path, value}) {
let creature = Creatures.findOne(_id); let creature = Creatures.findOne(_id);
assertEditPermission(creature, this.userId); assertEditPermission(creature, this.userId);
Creatures.update(_id, { if (value === undefined || value === null){
$set: {[path.join('.')]: value}, Creatures.update(_id, {
}); $unset: {[path.join('.')]: 1},
});
} else {
Creatures.update(_id, {
$set: {[path.join('.')]: value},
});
}
}, },
}); });

View File

@@ -1,8 +1,15 @@
<template lang="html"> <template lang="html">
<dialog-base> <dialog-base :color="model.color">
<v-toolbar-title slot="toolbar"> <template slot="toolbar">
Creature Form Dialog <v-toolbar-title>
</v-toolbar-title> Creature Form Dialog
</v-toolbar-title>
<v-spacer />
<color-picker
:value="model.color"
@input="value => change({path: ['color'], value})"
/>
</template>
<div> <div>
<creature-form <creature-form
:model="model" :model="model"
@@ -27,11 +34,13 @@ import {updateCreature} from '/imports/api/creature/Creatures.js';
import DialogBase from '/imports/ui/dialogStack/DialogBase.vue'; import DialogBase from '/imports/ui/dialogStack/DialogBase.vue';
import CreatureForm from '/imports/ui/creature/CreatureForm.vue' import CreatureForm from '/imports/ui/creature/CreatureForm.vue'
import { assertEditPermission } from '/imports/api/creature/creaturePermissions.js'; import { assertEditPermission } from '/imports/api/creature/creaturePermissions.js';
import ColorPicker from '/imports/ui/components/ColorPicker.vue';
export default { export default {
components: { components: {
DialogBase, DialogBase,
CreatureForm, CreatureForm,
ColorPicker,
}, },
props: { props: {
_id: String, _id: String,
@@ -52,8 +61,16 @@ export default {
}, },
methods: { methods: {
change({path, value, ack}){ change({path, value, ack}){
updateCreature.call({_id: this._id, path, value}, (error, result) =>{ updateCreature.call({_id: this._id, path, value}, (error) =>{
ack && ack(error && error.reason || error); if (error){
if(ack){
ack(error && error.reason || error)
} else {
console.error(error)
}
} else if (ack) {
ack();
}
}); });
}, },
} }

View File

@@ -0,0 +1,247 @@
<template lang="html">
<v-toolbar
app
class="character-sheet-toolbar"
:color="toolbarColor"
:dark="isDark"
:light="!isDark"
tabs
dense
>
<v-toolbar-side-icon @click="toggleDrawer" />
<v-toolbar-title>
<v-fade-transition
mode="out-in"
>
<div :key="$store.state.pageTitle">
{{ $store.state.pageTitle }}
</div>
</v-fade-transition>
</v-toolbar-title>
<v-spacer />
<v-fade-transition
mode="out-in"
>
<div :key="$route.meta.title">
<v-toolbar-items v-if="creature">
<v-btn
v-if="editPermission"
flat
icon
@click="recompute(creature._id)"
>
<v-icon>refresh</v-icon>
</v-btn>
<v-menu
bottom
left
transition="slide-y-transition"
data-id="creature-menu"
>
<template #activator="{ on }">
<v-btn
icon
v-on="on"
>
<v-icon>more_vert</v-icon>
</v-btn>
</template>
<v-list v-if="editPermission">
<v-list-tile @click="deleteCharacter">
<v-list-tile-title>
<v-icon>delete</v-icon> Delete
</v-list-tile-title>
</v-list-tile>
<v-list-tile @click="showCharacterForm">
<v-list-tile-title>
<v-icon>create</v-icon> Edit details
</v-list-tile-title>
</v-list-tile>
<v-list-tile @click="showShareDialog">
<v-list-tile-title>
<v-icon>share</v-icon> Sharing
</v-list-tile-title>
</v-list-tile>
</v-list>
<v-list v-else>
<v-list-tile @click="unshareWithMe">
<v-list-tile-title>
<v-icon>delete</v-icon> Unshare with me
</v-list-tile-title>
</v-list-tile>
</v-list>
</v-menu>
</v-toolbar-items>
</div>
</v-fade-transition>
<v-fade-transition
slot="extension"
mode="out-in"
>
<div
:key="$route.meta.title"
style="width: 100%"
>
<v-tabs
v-if="creature"
slot="extension"
:value="value"
centered
grow
max="100px"
@change="e => $emit('input', e)"
>
<v-tab>
Stats
</v-tab>
<v-tab>
Features
</v-tab>
<v-tab>
Inventory
</v-tab>
<v-tab>
Spells
</v-tab>
<v-tab>
Persona
</v-tab>
<v-tab>
Tree
</v-tab>
</v-tabs>
</div>
</v-fade-transition>
</v-toolbar>
</template>
<script>
import Creatures from '/imports/api/creature/Creatures.js';
import removeCreature from '/imports/api/creature/removeCreature.js';
import { mapMutations } from 'vuex';
import { theme } from '/imports/ui/theme.js';
import { recomputeCreature } from '/imports/api/creature/computation/recomputeCreature.js';
import { assertEditPermission } from '/imports/api/creature/creaturePermissions.js';
import { updateUserSharePermissions } from '/imports/api/sharing/sharing.js';
import isDarkColor from '/imports/ui/utility/isDarkColor.js';
export default {
props: {
value: {
type: Number,
required: true,
},
},
data(){return {
theme,
}},
computed: {
creatureId(){
return this.$route.params.id;
},
toolbarColor(){
if (this.creature && this.creature.color){
return this.creature.color;
} else {
return this.$vuetify.theme.secondary;
}
},
isDark(){
return isDarkColor(this.toolbarColor);
},
},
methods: {
...mapMutations([
'toggleDrawer',
]),
recompute(charId){
recomputeCreature.call({charId});
},
showCharacterForm(){
this.$store.commit('pushDialogStack', {
component: 'creature-form-dialog',
elementId: 'creature-menu',
data: {
_id: this.creatureId,
},
});
},
showShareDialog(){
this.$store.commit('pushDialogStack', {
component: 'share-dialog',
elementId: 'creature-menu',
data: {
docRef: {
id: this.creatureId,
collection: 'creatures',
}
},
});
},
deleteCharacter(){
let that = this;
this.$store.commit('pushDialogStack', {
component: 'delete-confirmation-dialog',
elementId: 'creature-menu',
data: {
name: this.creature.name,
typeName: 'Character'
},
callback(confirmation){
if(!confirmation) return;
removeCreature.call({charId: that.creatureId}, (error) => {
if (error) {
console.error(error);
} else {
that.$router.push('/characterList');
}
});
}
});
},
unshareWithMe(){
updateUserSharePermissions.call({
docRef: {
collection: 'creatures',
id: this.creatureId,
},
userId: Meteor.userId(),
role: 'none',
}, (error) => {
if (error) {
console.error(error);
} else {
this.$router.push('/characterList');
}
});
},
},
meteor: {
$subscribe: {
'singleCharacter'(){
return [this.creatureId];
},
},
creature(){
return Creatures.findOne(this.creatureId);
},
editPermission(){
try {
assertEditPermission(this.creature, Meteor.userId());
return true;
} catch (e) {
return false;
}
},
},
}
</script>
<style lang="css">
.character-sheet-toolbar .v-tabs__container--grow .v-tabs__div {
max-width: 120px !important;
}
.character-sheet-toolbar .v-tabs__bar {
background: none !important;
}
</style>

View File

@@ -2,7 +2,7 @@
<div class="inventory"> <div class="inventory">
<column-layout> <column-layout>
<div> <div>
<toolbar-card color=""> <toolbar-card :color="$vuetify.theme.secondary">
<v-spacer slot="toolbar" /> <v-spacer slot="toolbar" />
<v-switch <v-switch
v-if="context.editPermission !== false" v-if="context.editPermission !== false"

View File

@@ -4,14 +4,17 @@
:light="!darkMode" :light="!darkMode"
> >
<v-navigation-drawer <v-navigation-drawer
v-if="$route.path !== '/countdown'"
v-model="drawer" v-model="drawer"
app app
> >
<Sidebar /> <Sidebar />
</v-navigation-drawer> </v-navigation-drawer>
<router-view
v-model="tabs"
name="toolbar"
/>
<v-toolbar <v-toolbar
v-if="$route.path !== '/countdown'" v-if="!$route.matched[0].components.toolbar"
app app
color="secondary" color="secondary"
dark dark
@@ -116,6 +119,9 @@
'toggleDrawer', 'toggleDrawer',
]), ]),
}, },
mounted(){
console.log(this.$route);
}
}; };
</script> </script>

View File

@@ -1,7 +1,6 @@
<template> <template>
<div class="sidebar"> <div class="sidebar">
<v-alert <v-alert
v-if="$route.path !== '/countdown'"
icon="priority_high" icon="priority_high"
type="error" type="error"
dismissible dismissible

View File

@@ -1,5 +1,4 @@
import { RouterFactory, nativeScrollBehavior } from 'meteor/akryum:vue-router2'; import { RouterFactory, nativeScrollBehavior } from 'meteor/akryum:vue-router2';
import LAUNCH_DATE from '/imports/constants/LAUNCH_DATE.js';
import { acceptInviteToken } from '/imports/api/users/Invites.js'; import { acceptInviteToken } from '/imports/api/users/Invites.js';
// Components // Components
@@ -10,8 +9,7 @@ import Library from '/imports/ui/pages/Library.vue';
import SingleLibraryPage from '/imports/ui/pages/SingleLibraryPage.vue' import SingleLibraryPage from '/imports/ui/pages/SingleLibraryPage.vue'
import SingleLibraryToolbarItems from '/imports/ui/library/SingleLibraryToolbarItems.vue' import SingleLibraryToolbarItems from '/imports/ui/library/SingleLibraryToolbarItems.vue'
import CharacterSheetPage from '/imports/ui/pages/CharacterSheetPage.vue'; import CharacterSheetPage from '/imports/ui/pages/CharacterSheetPage.vue';
import CharacterSheetToolbarItems from '/imports/ui/creature/character/CharacterSheetToolbarItems.vue'; import CharacterSheetToolbar from '/imports/ui/creature/character/CharacterSheetToolbar.vue';
import CharacterSheetToolbarExtension from '/imports/ui/creature/character/CharacterSheetToolbarExtension.vue';
import SignIn from '/imports/ui/pages/SignIn.vue' ; import SignIn from '/imports/ui/pages/SignIn.vue' ;
import Register from '/imports/ui/pages/Register.vue'; import Register from '/imports/ui/pages/Register.vue';
import IconAdmin from '/imports/ui/icons/IconAdmin.vue'; import IconAdmin from '/imports/ui/icons/IconAdmin.vue';
@@ -22,7 +20,6 @@ import InviteSuccess from '/imports/ui/pages/InviteSuccess.vue' ;
import InviteError from '/imports/ui/pages/InviteError.vue' ; import InviteError from '/imports/ui/pages/InviteError.vue' ;
import NotImplemented from '/imports/ui/pages/NotImplemented.vue'; import NotImplemented from '/imports/ui/pages/NotImplemented.vue';
import PatreonLevelTooLow from '/imports/ui/pages/PatreonLevelTooLow.vue'; import PatreonLevelTooLow from '/imports/ui/pages/PatreonLevelTooLow.vue';
import LaunchCountdown from '/imports/ui/pages/LaunchCountdown.vue';
let userSubscription = Meteor.subscribe('user'); let userSubscription = Meteor.subscribe('user');
@@ -91,17 +88,7 @@ function claimInvite(to, from, next){
} }
RouterFactory.configure(factory => { RouterFactory.configure(factory => {
factory.addRoutes([ factory.addRoutes([{
{
path: '/countdown',
name: 'Countdown',
components: {
default: LaunchCountdown,
},
meta: {
title: 'Countdown to Launch',
},
},{
path: '/', path: '/',
name: 'home', name: 'home',
components: { components: {
@@ -142,8 +129,7 @@ RouterFactory.configure(factory => {
path: '/character/:id/:urlName', path: '/character/:id/:urlName',
components: { components: {
default: CharacterSheetPage, default: CharacterSheetPage,
toolbarExtension: CharacterSheetToolbarExtension, toolbar: CharacterSheetToolbar,
toolbarItems: CharacterSheetToolbarItems,
}, },
meta: { meta: {
title: 'Character Sheet', title: 'Character Sheet',
@@ -152,8 +138,7 @@ RouterFactory.configure(factory => {
path: '/character/:id', path: '/character/:id',
components: { components: {
default: CharacterSheetPage, default: CharacterSheetPage,
toolbarExtension: CharacterSheetToolbarExtension, toolbar: CharacterSheetToolbar,
toolbarItems: CharacterSheetToolbarItems,
}, },
meta: { meta: {
title: 'Character Sheet', title: 'Character Sheet',
@@ -263,13 +248,10 @@ const router = routerFactory.create();
router.beforeEach((to, from, next) => { router.beforeEach((to, from, next) => {
let user = Meteor.user(); let user = Meteor.user();
if ( if (
to.path === '/countdown' ||
to.path === '/sign-in' || to.path === '/sign-in' ||
(user && user.roles && user.roles.includes('admin')) (user && user.roles && user.roles.includes('admin'))
){ ){
next(); next();
} else if (new Date() < LAUNCH_DATE){
next('/countdown');
} else { } else {
next(); next();
} }

View File

@@ -10,7 +10,7 @@ const theme = {
const darkTheme = { const darkTheme = {
primary: '#f44336', primary: '#f44336',
secondary: '#757575', secondary: '#212121',
accent: '#f44336', accent: '#f44336',
error: '#FF6D00', error: '#FF6D00',
warning: '#FFB300', warning: '#FFB300',