Added library list UI

This commit is contained in:
Stefan Zermatten
2019-05-10 13:05:21 +02:00
parent de183297fc
commit 9a194a20cb
11 changed files with 217 additions and 35 deletions

View File

@@ -1,5 +1,6 @@
import schema from '/imports/api/schema.js';
import SharingSchema from '/imports/api/sharing/SharingSchema.js';
import simpleSchemaMixin from '/imports/api/creature/mixins/simpleSchemaMixin.js';
/**
* Libraries are trees of library nodes where each node represents a character
@@ -23,4 +24,17 @@ LibrarySchema.extend(SharingSchema);
Libraries.attachSchema(LibrarySchema);
export default Libraries;
export { LibrarySchema };
const insertLibrary = new ValidatedMethod({
name: 'Libraries.methods.insert',
mixins: [
simpleSchemaMixin,
],
schema: LibrarySchema.omit('owner'),
run(library) {
library.owner = this.userId;
return Libraries.insert(library);
},
});
export { LibrarySchema, insertLibrary };

View File

@@ -10,21 +10,21 @@ const userSchema = schema({
type: Array,
optional: true,
},
"emails.$": {
'emails.$': {
type: Object,
},
"emails.$.address": {
'emails.$.address': {
type: String,
regEx: SimpleSchema.RegEx.Email,
},
"emails.$.verified": {
'emails.$.verified': {
type: Boolean,
},
registered_emails: {
type: Array,
optional: true,
},
"registered_emails.$": {
'registered_emails.$': {
type: Object,
blackbox: true,
},
@@ -40,7 +40,7 @@ const userSchema = schema({
type: Array,
optional: true,
},
"roles.$": {
'roles.$': {
type: String
},
// In order to avoid an 'Exception in setInterval callback' from Meteor
@@ -57,12 +57,20 @@ const userSchema = schema({
type: Boolean,
optional: true,
},
subscribedLibraries: {
type: Array,
defaultValue: [],
max: 100,
},
'subscribedLibraries.$': {
type: String,
},
});
Meteor.users.attachSchema(userSchema);
Meteor.users.generateApiKey = new ValidatedMethod({
name: "Users.methods.generateApiKey",
name: 'Users.methods.generateApiKey',
validate: null,
run(){
if(Meteor.isClient) return;
@@ -75,7 +83,7 @@ Meteor.users.generateApiKey = new ValidatedMethod({
});
Meteor.users.setDarkMode = new ValidatedMethod({
name: "Users.methods.setDarkMode",
name: 'Users.methods.setDarkMode',
validate: new SimpleSchema({
darkMode: { type: Boolean },
}).validator(),
@@ -86,7 +94,7 @@ Meteor.users.setDarkMode = new ValidatedMethod({
});
Meteor.users.sendVerificationEmail = new ValidatedMethod({
name: "Users.methods.sendVerificationEmail",
name: 'Users.methods.sendVerificationEmail',
validate: schema({
userId:{
type: String,
@@ -100,12 +108,12 @@ Meteor.users.sendVerificationEmail = new ValidatedMethod({
userId = this.userId || userId;
let user = Meteor.users.findOne();
if (!user) {
throw new Meteor.Error("User not found",
"Can't send a validation email to a user that does not exist");
throw new Meteor.Error('User not found',
'Can\'t send a validation email to a user that does not exist');
}
if (!_.some(user.emails, email => email.address === address)) {
throw new Meteor.Error("Email address not found",
"The specified email address wasn't found on this user account");
throw new Meteor.Error('Email address not found',
'The specified email address wasn\'t found on this user account');
}
Accounts.sendVerificationEmail(this.userId, address);
}

View File

@@ -1,23 +1,23 @@
import Libraries from "/imports/api/library/Libraries.js"
import Libraries from '/imports/api/library/Libraries.js';
const standardLibraryIds = [
"SRDLibraryGA3XWsd",
'SRDLibraryGA3XWsd',
];
Meteor.publish("standardLibraries", function(){
Meteor.publish('standardLibraries', function(){
return Libraries.find({_id: {$in: standardLibraryIds}});
});
Meteor.publish("standardLibraryItems", function(categoryKey){
Meteor.publish('standardLibraryItems', function(categoryKey){
return LibraryItems.find({
library: {$in: standardLibraryIds},
"settings.category": categoryKey,
'settings.category': categoryKey,
}, {
sort: {name: 1},
});
});
Meteor.publish("standardLibrarySpells", function(level){
Meteor.publish('standardLibrarySpells', function(level){
return LibrarySpells.find({
library: {$in: standardLibraryIds},
level,
@@ -25,3 +25,17 @@ Meteor.publish("standardLibrarySpells", function(level){
sort: {name: 1},
});
});
Meteor.publish('libraries', function(){
const user = Meteor.user();
const userId = user && user._id;
const subs = user && user.subscribedLibraries || [];
return Libraries.find({
$or: [
{owner: userId},
{writers: userId},
{readers: userId},
{_id: {$in: subs}},
]
});
});

View File

@@ -5,7 +5,7 @@
{{userName}}
<v-spacer></v-spacer>
<v-tooltip bottom>
<v-btn flat icon slot="activator"><v-icon>settings</v-icon></v-btn>
<v-btn flat icon slot="activator" to="/account"><v-icon>settings</v-icon></v-btn>
<span>Account Settings</span>
</v-tooltip>
</v-layout>
@@ -82,6 +82,7 @@
return [
{title: "Home", icon: "home", to: "/"},
{title: "Creatures", icon: "group", to: "/characterList", vif: Meteor.userId()},
{title: "Libraries", icon: "book", to: "/library", vif: Meteor.userId()},
{title: "Send Feedback", icon: "bug_report", to: "/feedback"},
{title: "Patreon", icon: "", href: "https://www.patreon.com/dicecloud"},
{title: "Github", icon: "", href: "https://github.com/ThaumRystra/DiceCloud1"},

View File

@@ -18,7 +18,7 @@ export default {
dirty: false,
safeValue: this.value,
inputValue: this.value,
}},
};},
props: {
value: [String, Number],
debounceTime: {

View File

@@ -3,6 +3,7 @@ import AttributeDialogContainer from '/imports/ui/creature/properties/attributes
import AttributeCreationDialog from '/imports/ui/creature/properties/attributes/AttributeCreationDialog.vue';
import FeatureCreationDialog from '/imports/ui/creature/properties/features/FeatureCreationDialog.vue';
import FeatureDialogContainer from '/imports/ui/creature/properties/features/FeatureDialogContainer.vue';
import LibraryCreationDialog from '/imports/ui/library/LibraryCreationDialog.vue';
import SkillDialogContainer from '/imports/ui/creature/properties/skills/SkillDialogContainer.vue';
export default {
@@ -11,5 +12,6 @@ export default {
AttributeCreationDialog,
FeatureCreationDialog,
FeatureDialogContainer,
LibraryCreationDialog,
SkillDialogContainer,
};

View File

@@ -0,0 +1,53 @@
<template lang="html">
<dialog-base>
<template slot="toolbar">
<div>
New Library
</div>
</template>
<template>
<text-field label="name" :value="library.name" @change="nameChanged" :debounce-time="0"/>
</template>
<template slot="actions">
<v-spacer/>
<v-btn
flat
:disabled="!valid"
@click="$store.dispatch('popDialogStack', library)"
>
Insert Library
</v-btn>
</template>
</dialog-base>
</template>
<script>
import DialogBase from '/imports/ui/dialogStack/DialogBase.vue';
export default {
data(){ return {
library: {
name: 'New Library',
},
valid: true,
}},
components: {
DialogBase,
},
methods: {
nameChanged(val, ack){
if (val){
this.library.name = val;
this.valid = true,
ack();
} else {
this.valid = false;
ack('Name is required')
}
},
},
};
</script>
<style lang="css" scoped>
</style>

View File

@@ -0,0 +1,71 @@
<template lang="html">
<toolbar-layout>
<span slot="toolbar">Libraries</span>
<v-card class="ma-4">
<v-list v-if="libraries.length">
<v-list-tile
v-for="library in libraries"
:key="library._id"
:to="library.url"
:data-id="library._id"
>
<v-list-tile-content>
<v-list-tile-title>
{{library.name}}
</v-list-tile-title>
</v-list-tile-content>
</v-list-tile>
</v-list>
<v-card-text v-else-if="$subReady.libraries">
You aren't subscribed to any libraries :O
</v-card-text>
<v-card-text v-if="!$subReady.libraries" class="layout row justify-center">
<v-progress-circular indeterminate/>
</v-card-text>
</v-card>
<v-btn fixed fab bottom right
color="primary"
@click="insertLibrary"
data-id="insert-library-fab"
>
<v-icon>add</v-icon>
</v-btn>
</toolbar-layout>
</template>
<script>
import ToolbarLayout from '/imports/ui/layouts/ToolbarLayout.vue';
import Libraries, {insertLibrary} from '/imports/api/library/Libraries.js';
export default {
components: {
ToolbarLayout,
},
methods: {
insertLibrary(){
this.$store.commit('pushDialogStack', {
component: 'library-creation-dialog',
elementId: 'insert-library-fab',
callback(library){
if (!library) return;
let libraryId = insertLibrary.call(library);
return libraryId;
}
});
},
},
meteor: {
$subscribe: {
'libraries': [],
},
libraries(){
return Libraries.find({}, {
sort: {name: 1}
}).fetch();
}
}
};
</script>
<style lang="css" scoped>
</style>

View File

@@ -1,13 +1,14 @@
import { RouterFactory, nativeScrollBehavior } from 'meteor/akryum:vue-router2';
import Vue from "vue";
import Vue from 'vue';
// Components
import Home from '/imports/ui/pages/Home.vue';
import CharacterList from "/imports/ui/pages/CharacterList.vue";
import CharacterSheetPage from "/imports/ui/pages/CharacterSheetPage.vue";
import SignIn from "/imports/ui/pages/SignIn.vue" ;
import Register from "/imports/ui/pages/Register.vue" ;
import Account from "/imports/ui/pages/Account.vue" ;
import CharacterList from '/imports/ui/pages/CharacterList.vue';
import Libraries from '/imports/ui/pages/Libraries.vue';
import CharacterSheetPage from '/imports/ui/pages/CharacterSheetPage.vue';
import SignIn from '/imports/ui/pages/SignIn.vue' ;
import Register from '/imports/ui/pages/Register.vue' ;
import Account from '/imports/ui/pages/Account.vue' ;
// Not found
import NotFound from '/imports/ui/pages/NotFound.vue';
@@ -27,22 +28,25 @@ RouterFactory.configure(factory => {
name: 'home',
component: Home,
},{
path: "/characterList",
path: '/characterList',
component: CharacterList,
},{
path: "/character/:id/:urlName",
path: '/library',
component: Libraries,
},{
path: '/character/:id/:urlName',
component: CharacterSheetPage,
},{
path: "/character/:id",
path: '/character/:id',
component: CharacterSheetPage,
},{
path: "/sign-in",
path: '/sign-in',
component: SignIn,
},{
path: "/register",
path: '/register',
component: Register,
},{
path: "/account",
path: '/account',
component: Account,
},
]);
@@ -61,7 +65,7 @@ RouterFactory.configure(factory => {
component: StoryBook,
}, {
path: '/icon-admin',
name: "iconAdmin",
name: 'iconAdmin',
component: IconAdmin,
},
]);

View File

@@ -0,0 +1,14 @@
/**
* Some componets are used as targets for dialog animations often, and thus
* require a background color. Inheriting the color from the nearest ancestor
* that is known to have a background ensures that the background is
* consistent for both light and dark themes
**/
.v-list > div {
background-color: inherit;
}
.v-list > div > .v-list__tile {
background-color: inherit;
}

View File

@@ -1 +1,2 @@
import "./speedDial.css";
import './speedDial.css';
import './inheritBackgrounds.css';