Improved handling of tiers, made guest tier funcitonal, improved invites

This commit is contained in:
Thaum Rystra
2020-05-12 15:28:43 +02:00
parent bbda0ea1b6
commit b6c7ea8c4f
9 changed files with 126 additions and 69 deletions

View File

@@ -145,7 +145,41 @@ const acceptInviteToken = new ValidatedMethod({
},
});
const revokeInvite = new ValidatedMethod({
name: 'Invites.methods.revokeInvite',
validate: new SimpleSchema({
inviteId: {
type: String,
regEx: SimpleSchema.RegEx.Id,
},
}).validator(),
run({inviteId}) {
if (!this.userId) {
throw new Meteor.Error('Invites.methods.revokeInvite.denied',
'You need to be the logged in to revoke a token');
}
if (Meteor.isClient) return;
let invite = Invites.findOne(inviteId);
if (!invite){
throw new Meteor.Error('Invites.methods.revokeInvite.notFound',
'No invite could be found for this id');
}
if (this.userId !== invite.inviter) {
throw new Meteor.Error('Invites.methods.revokeInvite.denied',
'You are not the owner of this invite');
}
// If the invitee is empty, the token has already been revoked
if (!invite.invitee){
return;
}
Invites.update(invite._id, {
$unset: {invitee: 1, dateConfirmed: 1},
});
},
});
Invites.attachSchema(InviteSchema);
export default Invites;
export { alignInvitesWithPatreonTier, getInviteToken, acceptInviteToken };
export { alignInvitesWithPatreonTier, getInviteToken, acceptInviteToken, revokeInvite };

View File

@@ -1,5 +1,6 @@
import { findLast } from 'lodash';
import getEntitledCents from '/imports/api/users/patreon/getEntitledCents.js';
import Invites from '/imports/api/users/Invites.js';
const TIERS = [
{
@@ -62,7 +63,14 @@ export function getUserTier(user){
if (!user) throw 'User not found';
}
const entitledCents = getEntitledCents(user);
return getTierByEntitledCents(entitledCents);
const tier = getTierByEntitledCents(entitledCents);
if (tier.paidBenefits) return tier;
let invite = Invites.findOne({invitee: user._id, isFunded: true});
if (invite){
return GUEST_TIER;
} else {
return tier;
}
}
export default TIERS;

View File

@@ -18,6 +18,10 @@ Meteor.publish('user', function(){
{inviter: this.userId},
{invitee: this.userId}
],
}, {
fields: {
inviteToken: 0,
}
}),
];
});

View File

@@ -49,26 +49,12 @@
<v-subheader>
Patreon
</v-subheader>
<template v-if="user.services.patreon">
<v-list-tile>
<v-list-tile-title>
Pledged amount: ${{ entitledCents/100 }}
</v-list-tile-title>
<!--
<v-list-tile-action>
<v-btn icon>
<v-icon>refresh</v-icon>
</v-btn>
</v-list-tile-action>
-->
</v-list-tile>
<v-list-tile>
<v-list-tile-title>
Tier: {{ tier.name }}
</v-list-tile-title>
</v-list-tile>
</template>
<v-list-tile v-else>
<v-list-tile>
<v-list-tile-title>
Tier: {{ tier.name }}
</v-list-tile-title>
</v-list-tile>
<v-list-tile v-if="!user.services.patreon">
<v-btn @click="linkWithPatreon">
Link Patreon
</v-btn>
@@ -130,7 +116,10 @@
export default {
meteor: {
$subscribe: {
'user': [],
'userPublicProfiles'(){
if (!this.invites) return false;
return [this.invites.map(i => i.invitee).filter(i => !!i)];
},
},
user(){
return Meteor.user();
@@ -143,9 +132,9 @@
const user = Meteor.user();
return user && user.emails;
},
darkMode(){
return this.user && this.user.darkMode;
},
darkMode(){
return this.user && this.user.darkMode;
},
invites(){
let usernames = {};
Meteor.users.find({}).forEach(user => {
@@ -154,12 +143,12 @@
return Invites.find({
inviter: Meteor.userId(),
}, {
sort: {dateConfirmed: 1},
sort: {dateConfirmed: 1, invitee: -1},
}).map(invite => {
invite.inviteeName = usernames[invite.invitee];
return invite;
});
}
},
},
data(){ return {
showApiKey: false,
@@ -186,9 +175,9 @@
Meteor.logout();
router.push('/');
},
setDarkMode(value){
Meteor.users.setDarkMode.call({darkMode: !!value});
},
setDarkMode(value){
Meteor.users.setDarkMode.call({darkMode: !!value});
},
generateKey(){
Meteor.users.gnerateApiKey.call(error => {
if(error) this.apiKeyGenerationError = error.reason;
@@ -202,10 +191,10 @@
},
clickInvite(invite){
this.$store.commit('pushDialogStack', {
component: 'invite-dialog',
elementId: invite._id,
data: {inviteId: invite._id},
});
component: 'invite-dialog',
elementId: invite._id,
data: {inviteId: invite._id},
});
},
linkWithPatreon,
},

View File

@@ -100,7 +100,7 @@
large
to="/sign-in"
>
Sign up
Sign In
</v-btn>
</v-layout>
</section>

View File

@@ -6,25 +6,26 @@
justify-center
>
<h2 style="margin: 48px 28px 16px">
Your current patreon support is ${{ entitledDollars }}.
</h2>
<h2 style="margin: 16px 28px">
You need to pledge at least $5 to use this beta.
Your current Patreon tier is {{ tier.name }}
</h2>
<h3>
You need to be at least Adventurer tier (or be invited by a Patron of
a higher tier) to access this beta
</h3>
</v-layout>
</div>
</template>
<script>
import TIERS, { getUserTier } from '/imports/api/users/patreon/tiers.js';
export default {
meteor: {
entitledDollars(){
tier(){
let user = Meteor.user();
if (!user) return 0;
let entitledCents = user.services.patreon.entitledCents || 0;
let overrideCents = user.services.patreon.entitledCentsOverride || 0;
return Math.max(entitledCents, overrideCents)/100;
if (!user) return TIERS[0];
return getUserTier(user);
}
}
},
}
</script>

View File

@@ -13,7 +13,6 @@
width="120px"
class="ma-3"
/>
<!--
<v-text-field
v-model="name"
type="text"
@@ -51,7 +50,6 @@
Register
</v-btn>
</v-layout>
-->
</v-layout>
</v-form>
<v-divider class="ma-4" />
@@ -59,7 +57,6 @@
column
align-center
>
<!--
<div class="error--text">
{{ googleError }}
</div>
@@ -69,7 +66,6 @@
>
Sign in with Google
</v-btn>
-->
<div class="error--text">
{{ patreonError }}
</div>

View File

@@ -1,5 +1,5 @@
import { RouterFactory, nativeScrollBehavior } from 'meteor/akryum:vue-router2';
import getEntitledCents from '/imports/api/users/patreon/getEntitledCents.js';
import { getUserTier } from '/imports/api/users/patreon/tiers.js';
import LAUNCH_DATE from '/imports/constants/LAUNCH_DATE.js';
import { acceptInviteToken } from '/imports/api/users/Invites.js';
@@ -48,7 +48,7 @@ function ensureLoggedIn(to, from, next){
});
}
function ensurePatronTier5(to, from, next){
function ensurePaidFeatures(to, from, next){
Tracker.autorun((computation) => {
if (userSubscription.ready()){
computation.stop();
@@ -57,11 +57,11 @@ function ensurePatronTier5(to, from, next){
next({ name: 'signIn', query: { redirect: to.path} });
return;
}
let entitledCents = getEntitledCents(user);
if (entitledCents < 500){
next('/patreon-level-too-low');
} else {
let tier = getUserTier(user);
if (tier && tier.paidBenefits){
next();
} else {
next('/patreon-level-too-low');
}
}
});
@@ -118,7 +118,7 @@ RouterFactory.configure(factory => {
meta: {
title: 'Character List',
},
beforeEnter: ensurePatronTier5,
beforeEnter: ensurePaidFeatures,
},{
path: '/library',
components: {
@@ -127,7 +127,7 @@ RouterFactory.configure(factory => {
meta: {
title: 'Library',
},
beforeEnter: ensurePatronTier5,
beforeEnter: ensurePaidFeatures,
},{
name: 'singleLibrary',
path: '/library/:id',
@@ -138,7 +138,7 @@ RouterFactory.configure(factory => {
meta: {
title: 'Library',
},
beforeEnter: ensurePatronTier5,
beforeEnter: ensurePaidFeatures,
},{
path: '/character/:id/:urlName',
components: {
@@ -149,7 +149,7 @@ RouterFactory.configure(factory => {
meta: {
title: 'Character Sheet',
},
beforeEnter: ensurePatronTier5,
beforeEnter: ensurePaidFeatures,
},{
path: '/character/:id',
components: {
@@ -160,7 +160,7 @@ RouterFactory.configure(factory => {
meta: {
title: 'Character Sheet',
},
beforeEnter: ensurePatronTier5,
beforeEnter: ensurePaidFeatures,
},{
path: '/friends',
components: {
@@ -179,15 +179,15 @@ RouterFactory.configure(factory => {
meta: {
title: 'Sign In',
},
},/*{
},{
path: '/register',
components: {
default: Register,
},
meta: {
title: 'Home',
title: 'Register',
},
},*/{
},{
path: '/account',
components: {
default: Account,

View File

@@ -5,12 +5,29 @@
Invite
</v-toolbar-title>
</template>
<div v-if="invite.invitee" />
<div v-else>
<div
v-if="invite.invitee"
class="layout column align-center"
>
{{ username || invite.invitee }}
<div>
<v-btn
color="primary"
@click="revokeInvite"
>
Revoke Invite
</v-btn>
</div>
</div>
<div
v-else
class="layout column align-center"
>
<p>This invite is available</p>
<v-fade-transition mode="out-in">
<v-btn
v-if="!inviteLink"
color="primary"
:loading="loading"
:disabled="loading"
@click="getInviteLink"
@@ -27,7 +44,7 @@
<script>
import DialogBase from '/imports/ui/dialogStack/DialogBase.vue';
import Invites, { getInviteToken } from '/imports/api/users/Invites.js';
import Invites, { getInviteToken, revokeInvite } from '/imports/api/users/Invites.js';
export default {
components: {
@@ -47,6 +64,11 @@ export default {
meteor: {
invite(){
return Invites.findOne(this.inviteId);
},
username(){
if (!this.invite) return;
let user = Meteor.users.findOne(this.invite.invitee);
return user && user.username;
}
},
computed: {
@@ -67,8 +89,11 @@ export default {
this.inviteToken = result;
}
});
}
}
},
revokeInvite(){
revokeInvite.call({inviteId: this.inviteId});
},
},
}
</script>