Made all login Patreon only, limited some functionality to $5 patrons
This commit is contained in:
@@ -47,3 +47,4 @@ static-html
|
||||
aldeed:collection2@3.0.0
|
||||
aldeed:schema-index
|
||||
akryum:vue-component
|
||||
accounts-patreon
|
||||
|
||||
@@ -2,6 +2,7 @@ accounts-base@1.6.0
|
||||
accounts-google@1.3.3
|
||||
accounts-oauth@1.2.0
|
||||
accounts-password@1.6.0
|
||||
accounts-patreon@0.1.0
|
||||
accounts-ui@1.3.1
|
||||
accounts-ui-unstyled@1.4.2
|
||||
akryum:npm-check@0.1.2
|
||||
@@ -88,6 +89,7 @@ oauth2@1.3.0
|
||||
observe-sequence@1.0.16
|
||||
ongoworks:speakingurl@9.0.0
|
||||
ordered-dict@1.1.0
|
||||
patreon-oauth@0.1.0
|
||||
percolate:migrations@0.9.8
|
||||
percolate:synced-cron@1.3.2
|
||||
promise@0.11.2
|
||||
|
||||
@@ -1,15 +1,18 @@
|
||||
import '/imports/api/users/Users.js';
|
||||
|
||||
Meteor.publish("user", function(){
|
||||
Meteor.publish('user', function(){
|
||||
return Meteor.users.find(this.userId, {fields: {
|
||||
roles: 1,
|
||||
username: 1,
|
||||
apiKey: 1,
|
||||
darkMode: 1,
|
||||
'services.patreon.id': 1,
|
||||
'services.patreon.entitledCents': 1,
|
||||
'services.patreon.entitledCentsOverride': 1,
|
||||
}});
|
||||
});
|
||||
|
||||
Meteor.publish("userPublicProfiles", function(ids){
|
||||
Meteor.publish('userPublicProfiles', function(ids){
|
||||
if (!this.userId || !Array.isArray(ids)) return [];
|
||||
return Meteor.users.find({
|
||||
_id: {$in: ids}
|
||||
|
||||
@@ -1,100 +0,0 @@
|
||||
<template lang="html">
|
||||
<toolbar-layout>
|
||||
<v-layout row slot="toolbar" align-center>
|
||||
<div>
|
||||
Storybook
|
||||
</div>
|
||||
<v-flex/>
|
||||
<v-btn flat icon @click="sidebar = !sidebar">
|
||||
<v-icon>menu</v-icon>
|
||||
</v-btn>
|
||||
</v-layout>
|
||||
<v-navigation-drawer right app v-model="sidebar">
|
||||
<v-toolbar color="secondary" dark>
|
||||
Components
|
||||
<v-switch :input-value="darkMode" @change="setDarkMode" label="Dark mode"/>
|
||||
</v-toolbar>
|
||||
<v-list>
|
||||
<v-list-tile
|
||||
v-for="(component, componentName) in $options.components"
|
||||
v-if="componentName !== 'story-book' && componentName !== 'ToolbarLayout'"
|
||||
:key="componentName"
|
||||
@click=""
|
||||
:to="`/storybook/${componentName}`"
|
||||
>
|
||||
<v-list-tile-content>
|
||||
<v-list-tile-title>{{componentName}}</v-list-tile-title>
|
||||
</v-list-tile-content>
|
||||
</v-list-tile>
|
||||
</v-list>
|
||||
</v-navigation-drawer>
|
||||
<div class="content">
|
||||
<component v-if="dontWrap" :is="$route.params.component"/>
|
||||
<v-card class="ma-4" v-else="dontWrap">
|
||||
<component :is="$route.params.component"/>
|
||||
</v-card>
|
||||
</div>
|
||||
</toolbar-layout>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Vue from "vue";
|
||||
import AbilityListTile from '/imports/ui/properties/components/attributes/AbilityListTile.Story.vue';
|
||||
import AttributeCard from '/imports/ui/properties/components/attributes/AttributeCard.Story.vue';
|
||||
import ColorPicker from '/imports/ui/components/ColorPicker.Story.vue';
|
||||
import ColumnLayout from "/imports/ui/components/ColumnLayout.Story.vue";
|
||||
import DialogStack from '/imports/ui/dialogStack/DialogStack.Story.vue';
|
||||
import EffectEditExpansionList from '/imports/ui/properties/viewers/shared/effects/EffectEditExpansionList.Story.vue';
|
||||
import FeatureCard from '/imports/ui/properties/components/features/FeatureCard.Story.vue';
|
||||
import HealthBar from '/imports/ui/properties/components/attributes/HealthBar.Story.vue';
|
||||
import HitDiceListTile from '/imports/ui/properties/components/attributes/HitDiceListTile.Story.vue';
|
||||
// import IconSearch from '/imports/ui/components/IconSearch.Story.vue';
|
||||
import ResourceCard from '/imports/ui/properties/components/attributes/ResourceCard.Story.vue';
|
||||
import SkillListTile from '/imports/ui/properties/components/skills/SkillListTile.Story.vue';
|
||||
import SmartInput from '/imports/ui/components/global/SmartInput.Story.vue';
|
||||
import SpellSlotListTile from '/imports/ui/properties/components/attributes/SpellSlotListTile.Story.vue';
|
||||
import ToolbarLayout from '/imports/ui/layouts/ToolbarLayout.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
AbilityListTile,
|
||||
AttributeCard,
|
||||
ColorPicker,
|
||||
ColumnLayout,
|
||||
DialogStack,
|
||||
EffectEditExpansionList,
|
||||
FeatureCard,
|
||||
HealthBar,
|
||||
HitDiceListTile,
|
||||
// IconSearch,
|
||||
ResourceCard,
|
||||
SkillListTile,
|
||||
SmartInput,
|
||||
SpellSlotListTile,
|
||||
ToolbarLayout,
|
||||
},
|
||||
data(){ return {
|
||||
sidebar: undefined,
|
||||
}},
|
||||
computed: {
|
||||
dontWrap(){
|
||||
let component = this.$options.components[this.$route.params.component];
|
||||
return component && component.dontWrap;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
setDarkMode(value){
|
||||
Meteor.users.setDarkMode.call({darkMode: !!value});
|
||||
},
|
||||
},
|
||||
meteor: {
|
||||
darkMode(){
|
||||
let user = Meteor.user();
|
||||
return user && user.darkMode;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="css" scoped>
|
||||
</style>
|
||||
@@ -1,59 +1,77 @@
|
||||
<template>
|
||||
<div class="sidebar">
|
||||
<v-alert
|
||||
:value="showWarning"
|
||||
type="warning"
|
||||
>
|
||||
<div>
|
||||
This is an early build of DiceCloud version 2. Data will be erased
|
||||
frequently. Don't store anything important here.
|
||||
</div>
|
||||
<div class="layout row justify-center">
|
||||
<v-btn @click="showWarning = false">I won't</v-btn>
|
||||
</div>
|
||||
</v-alert>
|
||||
<v-layout row justify-center v-if="!signedIn">
|
||||
<v-btn flat to="/sign-in">Sign in</v-btn>
|
||||
</v-layout>
|
||||
<v-alert
|
||||
:value="showWarning"
|
||||
type="warning"
|
||||
>
|
||||
<div>
|
||||
This is an early build of DiceCloud version 2. Data will be erased
|
||||
frequently. Don't store anything important here yet.
|
||||
</div>
|
||||
<div class="layout row justify-center">
|
||||
<v-btn @click="showWarning = false">
|
||||
I won't
|
||||
</v-btn>
|
||||
</div>
|
||||
</v-alert>
|
||||
<v-layout
|
||||
v-if="!signedIn"
|
||||
row
|
||||
justify-center
|
||||
>
|
||||
<v-btn
|
||||
flat
|
||||
to="/sign-in"
|
||||
>
|
||||
Sign in
|
||||
</v-btn>
|
||||
</v-layout>
|
||||
<v-list>
|
||||
<v-list-tile v-if="signedIn">
|
||||
<v-list-tile-content>
|
||||
<v-list-tile-title>
|
||||
{{userName}}
|
||||
</v-list-tile-title>
|
||||
<v-list-tile v-if="signedIn">
|
||||
<v-list-tile-content>
|
||||
<v-list-tile-title>
|
||||
{{ userName }}
|
||||
</v-list-tile-title>
|
||||
</v-list-tile-content>
|
||||
<v-list-tile-action>
|
||||
<v-tooltip bottom>
|
||||
<v-btn flat icon slot="activator" to="/account"><v-icon>settings</v-icon></v-btn>
|
||||
<span>Account Settings</span>
|
||||
</v-tooltip>
|
||||
<v-list-tile-action>
|
||||
<v-tooltip bottom>
|
||||
<v-btn
|
||||
slot="activator"
|
||||
flat
|
||||
icon
|
||||
to="/account"
|
||||
>
|
||||
<v-icon>settings</v-icon>
|
||||
</v-btn>
|
||||
<span>Account Settings</span>
|
||||
</v-tooltip>
|
||||
</v-list-tile-action>
|
||||
</v-list-tile>
|
||||
</v-list-tile>
|
||||
|
||||
<v-list-tile
|
||||
v-for="(link, i) in links"
|
||||
v-if="link.vif || link.vif === undefined"
|
||||
:to="link.to"
|
||||
:href="link.href"
|
||||
v-if="link.vif || link.vif === undefined"
|
||||
:key="i"
|
||||
:to="link.to"
|
||||
:href="link.href"
|
||||
>
|
||||
<v-list-tile-action>
|
||||
<v-icon>{{link.icon}}</v-icon>
|
||||
<v-icon>{{ link.icon }}</v-icon>
|
||||
</v-list-tile-action>
|
||||
<v-list-tile-title>
|
||||
{{link.title}}
|
||||
{{ link.title }}
|
||||
</v-list-tile-title>
|
||||
</v-list-tile>
|
||||
<v-divider></v-divider>
|
||||
<v-divider />
|
||||
</v-list>
|
||||
<v-list dense>
|
||||
<v-list-tile
|
||||
v-for="character in CreaturesWithNoParty"
|
||||
:to="character.url"
|
||||
:key="character._id"
|
||||
:to="character.url"
|
||||
>
|
||||
<v-list-tile-title>
|
||||
{{character.name}}
|
||||
{{ character.name }}
|
||||
</v-list-tile-title>
|
||||
</v-list-tile>
|
||||
<v-list-group
|
||||
@@ -62,16 +80,16 @@
|
||||
>
|
||||
<v-list-tile slot="activator">
|
||||
<v-list-tile-title>
|
||||
{{party.name}}
|
||||
{{ party.name }}
|
||||
</v-list-tile-title>
|
||||
</v-list-tile>
|
||||
<v-list-tile
|
||||
v-for="character in characterDocs"
|
||||
:to="character.url"
|
||||
:key="character._id"
|
||||
:to="character.url"
|
||||
>
|
||||
<v-list-tile-title>
|
||||
{{character.name}}
|
||||
{{ character.name }}
|
||||
</v-list-tile-title>
|
||||
</v-list-tile>
|
||||
</v-list-group>
|
||||
@@ -89,7 +107,7 @@
|
||||
}},
|
||||
meteor: {
|
||||
$subscribe: {
|
||||
"characterList": [],
|
||||
'characterList': [],
|
||||
},
|
||||
signedIn(){
|
||||
return Meteor.userId();
|
||||
@@ -100,16 +118,13 @@
|
||||
},
|
||||
links(){
|
||||
let links = [
|
||||
{title: "Home", icon: "home", to: "/"},
|
||||
{title: "Characters", icon: "group", to: "/characterList", vif: Meteor.userId()},
|
||||
{title: "Library", 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/DiceCloud/tree/version-2"},
|
||||
{title: 'Home', icon: 'home', to: '/'},
|
||||
{title: 'Characters', icon: 'group', to: '/characterList', vif: Meteor.userId()},
|
||||
{title: 'Library', 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/DiceCloud/tree/version-2'},
|
||||
];
|
||||
if (Meteor.settings.public.showStorybook || Meteor.isDevelopment){
|
||||
links.push({title: 'Component Previews', icon: "category", to: '/storybook/HealthBar'})
|
||||
}
|
||||
return links;
|
||||
},
|
||||
parties(){
|
||||
@@ -126,7 +141,7 @@
|
||||
fields: {name: 1, urlName: 1},
|
||||
}
|
||||
).map(char => {
|
||||
char.url = `\/character\/${char._id}\/${char.urlName || "-"}`;
|
||||
char.url = `\/character\/${char._id}\/${char.urlName || '-'}`;
|
||||
return char;
|
||||
});
|
||||
return party;
|
||||
@@ -143,7 +158,7 @@
|
||||
},
|
||||
{sort: {name: 1}}
|
||||
).map(char => {
|
||||
char.url = `\/character\/${char._id}\/${char.urlName || "-"}`;
|
||||
char.url = `\/character\/${char._id}\/${char.urlName || '-'}`;
|
||||
return char;
|
||||
});
|
||||
},
|
||||
|
||||
@@ -3,23 +3,34 @@
|
||||
<span slot="toolbar">
|
||||
Account
|
||||
</span>
|
||||
<v-layout align-center justify-center>
|
||||
<v-layout
|
||||
align-center
|
||||
justify-center
|
||||
>
|
||||
<v-card class="ma-4 pa-2">
|
||||
<v-list>
|
||||
<v-list-tile>
|
||||
<v-switch :input-value="darkMode" @change="setDarkMode" label="Dark mode"/>
|
||||
</v-list-tile>
|
||||
<v-list-tile>
|
||||
<v-switch
|
||||
:input-value="darkMode"
|
||||
label="Dark mode"
|
||||
@change="setDarkMode"
|
||||
/>
|
||||
</v-list-tile>
|
||||
<v-subheader>
|
||||
Username
|
||||
</v-subheader>
|
||||
<v-list-tile>
|
||||
<v-list-tile-title>
|
||||
{{user.username}}
|
||||
{{ user && user.username }}
|
||||
</v-list-tile-title>
|
||||
<v-list-tile-action>
|
||||
<v-tooltip left>
|
||||
<span>Change Username</span>
|
||||
<v-btn icon flat slot="activator">
|
||||
<v-btn
|
||||
slot="activator"
|
||||
icon
|
||||
flat
|
||||
>
|
||||
<v-icon>create</v-icon>
|
||||
</v-btn>
|
||||
</v-tooltip>
|
||||
@@ -28,21 +39,29 @@
|
||||
<v-subheader>
|
||||
Email
|
||||
</v-subheader>
|
||||
<v-list-tile v-for="email in emails" :key="email.address">
|
||||
<v-list-tile
|
||||
v-for="email in emails"
|
||||
:key="email.address"
|
||||
>
|
||||
<v-list-tile-title>
|
||||
{{email.address}}
|
||||
{{ email.address }}
|
||||
</v-list-tile-title>
|
||||
<v-list-tile-action>
|
||||
<v-tooltip left v-if="email.verified">
|
||||
<v-tooltip
|
||||
v-if="email.verified"
|
||||
left
|
||||
>
|
||||
<span>Verified</span>
|
||||
<v-icon slot="activator">assignment_turned_in</v-icon>
|
||||
<v-icon slot="activator">
|
||||
assignment_turned_in
|
||||
</v-icon>
|
||||
</v-tooltip>
|
||||
<v-tooltip left v-else="email.verified">
|
||||
<v-tooltip left>
|
||||
<span>Verify Account</span>
|
||||
<v-btn
|
||||
slot="activator"
|
||||
flat
|
||||
icon
|
||||
slot="activator"
|
||||
@click="verifyEmail(email.address)"
|
||||
>
|
||||
<v-icon>assignment_late</v-icon>
|
||||
@@ -54,7 +73,11 @@
|
||||
<v-list-tile-action>
|
||||
<v-tooltip right>
|
||||
<span>Add email address</span>
|
||||
<v-btn flat icon slot="activator">
|
||||
<v-btn
|
||||
slot="activator"
|
||||
flat
|
||||
icon
|
||||
>
|
||||
<v-icon>add</v-icon>
|
||||
</v-btn>
|
||||
</v-tooltip>
|
||||
@@ -63,24 +86,33 @@
|
||||
<v-subheader>
|
||||
API Key
|
||||
</v-subheader>
|
||||
<v-list-tile v-if="user.apiKey">
|
||||
<v-list-tile v-if="user && user.apiKey">
|
||||
<v-list-tile v-if="showApiKey">
|
||||
{{user.apiKey}}
|
||||
{{ user.apiKey }}
|
||||
</v-list-tile>
|
||||
<v-list-tile-title v-else="showApiKey">
|
||||
{{"•".repeat(user.apiKey.length)}}
|
||||
<v-list-tile-title>
|
||||
{{ "•".repeat(user.apiKey.length) }}
|
||||
</v-list-tile-title>
|
||||
<v-list-tile-action>
|
||||
<v-btn flat icon @click="showApiKey=!showApiKey">
|
||||
<v-icon>{{showApiKey ? 'visibility_off' : 'visibility'}}</v-icon>
|
||||
<v-btn
|
||||
flat
|
||||
icon
|
||||
@click="showApiKey=!showApiKey"
|
||||
>
|
||||
<v-icon>{{ showApiKey ? 'visibility_off' : 'visibility' }}</v-icon>
|
||||
</v-btn>
|
||||
</v-list-tile-action>
|
||||
</v-list-tile>
|
||||
<v-list-tile v-else="user.apiKey">
|
||||
<v-btn flat color="accent" @click="generateKey">
|
||||
<v-list-tile v-else>
|
||||
<v-btn
|
||||
flat
|
||||
color="accent"
|
||||
@click="generateKey"
|
||||
>
|
||||
Generate API Key
|
||||
</v-btn>
|
||||
</v-list-tile>
|
||||
<!--
|
||||
<v-subheader>
|
||||
Google Account
|
||||
</v-subheader>
|
||||
@@ -90,22 +122,30 @@
|
||||
</v-list-tile-avatar>
|
||||
<v-list-tile-content>
|
||||
<v-list-tile-title>
|
||||
{{googleAccount.name}}
|
||||
{{ googleAccount.name }}
|
||||
</v-list-tile-title>
|
||||
<v-list-tile-sub-title>
|
||||
{{googleAccount.email}}
|
||||
{{ googleAccount.email }}
|
||||
</v-list-tile-sub-title>
|
||||
</v-list-tile-content>
|
||||
</v-list-tile>
|
||||
<v-list-tile v-else="googleAccount">
|
||||
<v-btn flat color="accent">
|
||||
<v-btn
|
||||
flat
|
||||
color="accent"
|
||||
>
|
||||
Connect Google
|
||||
</v-btn>
|
||||
</v-list-tile>
|
||||
-->
|
||||
</v-list>
|
||||
<v-card-actions>
|
||||
<v-spacer></v-spacer>
|
||||
<v-btn flat color="accent">
|
||||
<v-spacer />
|
||||
<v-btn
|
||||
flat
|
||||
color="accent"
|
||||
@click="signOut"
|
||||
>
|
||||
Sign Out
|
||||
</v-btn>
|
||||
</v-card-actions>
|
||||
@@ -115,28 +155,32 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ToolbarLayout from "/imports/ui/layouts/ToolbarLayout.vue";
|
||||
import { Accounts } from "meteor/accounts-base";
|
||||
import router from "/imports/ui/vueSetup.js";
|
||||
import router from '/imports/ui/router.js';
|
||||
import ToolbarLayout from '/imports/ui/layouts/ToolbarLayout.vue';
|
||||
|
||||
export default {
|
||||
meteor: {
|
||||
$subscribe: {
|
||||
"user": [],
|
||||
'user': [],
|
||||
},
|
||||
user(){
|
||||
return Meteor.user();
|
||||
},
|
||||
googleAccount(){
|
||||
const user = Meteor.user();
|
||||
return user.services && user.services.google;
|
||||
return user && user.services && user.services.google;
|
||||
},
|
||||
emails(){
|
||||
return Meteor.user().emails;
|
||||
const user = Meteor.user();
|
||||
return user && user.emails;
|
||||
},
|
||||
darkMode(){
|
||||
return this.user && this.user.darkMode;
|
||||
},
|
||||
},
|
||||
components: {
|
||||
ToolbarLayout,
|
||||
},
|
||||
data(){ return {
|
||||
showApiKey: false,
|
||||
signOutBusy: false,
|
||||
@@ -145,9 +189,8 @@
|
||||
}},
|
||||
methods: {
|
||||
signOut(){
|
||||
Meteor.logout(function (e) {
|
||||
|
||||
});
|
||||
Meteor.logout();
|
||||
router.push('/');
|
||||
},
|
||||
setDarkMode(value){
|
||||
Meteor.users.setDarkMode.call({darkMode: !!value});
|
||||
@@ -156,7 +199,7 @@
|
||||
Meteor.users.gnerateApiKey.call(error => {
|
||||
if(error) this.apiKeyGenerationError = error.reason;
|
||||
});
|
||||
this.showApiKey = true;
|
||||
this.showApiKey = true;
|
||||
},
|
||||
verifyEmail(address){
|
||||
Meteor.users.sendVerificationEmail.call({address}, error => {
|
||||
@@ -164,8 +207,5 @@
|
||||
});
|
||||
},
|
||||
},
|
||||
components: {
|
||||
ToolbarLayout,
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -5,8 +5,16 @@
|
||||
</div>
|
||||
<div class="content">
|
||||
<section>
|
||||
<v-parallax src="/png/paper-dice-crown.png" height="300">
|
||||
<v-layout column align-center justify-center class="white--text">
|
||||
<v-parallax
|
||||
src="/png/paper-dice-crown.png"
|
||||
height="300"
|
||||
>
|
||||
<v-layout
|
||||
column
|
||||
align-center
|
||||
justify-center
|
||||
class="white--text"
|
||||
>
|
||||
<h1 class="white--text mb-2 display-1 text-xs-center">
|
||||
DiceCloud - Free, Auditable, real-time character tracking for 5th edition
|
||||
</h1>
|
||||
@@ -17,9 +25,22 @@
|
||||
</v-parallax>
|
||||
</section>
|
||||
<section class="text-xs-center">
|
||||
<v-layout row wrap justify-space-around class="selling-points">
|
||||
<v-layout column align-center>
|
||||
<v-icon x-large class="ma-2">money_off</v-icon>
|
||||
<v-layout
|
||||
row
|
||||
wrap
|
||||
justify-space-around
|
||||
class="selling-points"
|
||||
>
|
||||
<v-layout
|
||||
column
|
||||
align-center
|
||||
>
|
||||
<v-icon
|
||||
x-large
|
||||
class="ma-2"
|
||||
>
|
||||
money_off
|
||||
</v-icon>
|
||||
<h3 class="mb-2">
|
||||
Free, open source, community funded
|
||||
</h3>
|
||||
@@ -28,8 +49,16 @@
|
||||
and the source code is available on Github under a GPL license.
|
||||
</p>
|
||||
</v-layout>
|
||||
<v-layout column align-center>
|
||||
<v-icon x-large class="ma-2">ballot</v-icon>
|
||||
<v-layout
|
||||
column
|
||||
align-center
|
||||
>
|
||||
<v-icon
|
||||
x-large
|
||||
class="ma-2"
|
||||
>
|
||||
ballot
|
||||
</v-icon>
|
||||
<h3 class="mb-2">
|
||||
Character sheets optimised for one ruleset
|
||||
</h3>
|
||||
@@ -38,8 +67,16 @@
|
||||
does: being a fully automated character tracker
|
||||
</p>
|
||||
</v-layout>
|
||||
<v-layout column align-center>
|
||||
<v-icon x-large class="ma-2">scatter_plot</v-icon>
|
||||
<v-layout
|
||||
column
|
||||
align-center
|
||||
>
|
||||
<v-icon
|
||||
x-large
|
||||
class="ma-2"
|
||||
>
|
||||
scatter_plot
|
||||
</v-icon>
|
||||
<h3 class="mb-2">
|
||||
Inventory manager
|
||||
</h3>
|
||||
@@ -51,9 +88,21 @@
|
||||
</v-layout>
|
||||
</v-layout>
|
||||
</section>
|
||||
<section class="ma-5" v-if="!signedIn">
|
||||
<v-layout row align-center justify-space-around>
|
||||
<v-btn color="accent" round large to="/register">
|
||||
<section
|
||||
v-if="!signedIn"
|
||||
class="ma-5"
|
||||
>
|
||||
<v-layout
|
||||
row
|
||||
align-center
|
||||
justify-space-around
|
||||
>
|
||||
<v-btn
|
||||
color="accent"
|
||||
round
|
||||
large
|
||||
to="/sign-in"
|
||||
>
|
||||
Sign up
|
||||
</v-btn>
|
||||
</v-layout>
|
||||
@@ -62,29 +111,34 @@
|
||||
<h1 class="mb-2 text-xs-center">
|
||||
Check out the example characters
|
||||
</h1>
|
||||
<v-layout row align-center justify-space-around class="pa-4">
|
||||
<v-layout
|
||||
row
|
||||
align-center
|
||||
justify-space-around
|
||||
class="pa-4"
|
||||
>
|
||||
<a href="/character/yBWwt5XQTTHZiRQxq">
|
||||
<v-hover>
|
||||
<v-card
|
||||
slot-scope="{ hover }"
|
||||
:class="`elevation-${hover ? 12 : 2}`"
|
||||
>
|
||||
<v-card-text>
|
||||
Starter set archer
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
<v-card
|
||||
slot-scope="{ hover }"
|
||||
:class="`elevation-${hover ? 12 : 2}`"
|
||||
>
|
||||
<v-card-text>
|
||||
Starter set archer
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</v-hover>
|
||||
</a>
|
||||
<a href="/character/yBWwt5XQTTHZiRQxq">
|
||||
<v-hover>
|
||||
<v-card
|
||||
slot-scope="{ hover }"
|
||||
:class="`elevation-${hover ? 12 : 2}`"
|
||||
>
|
||||
<v-card-text>
|
||||
Starter set wizard
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
<v-card
|
||||
slot-scope="{ hover }"
|
||||
:class="`elevation-${hover ? 12 : 2}`"
|
||||
>
|
||||
<v-card-text>
|
||||
Starter set wizard
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</v-hover>
|
||||
</a>
|
||||
</v-layout>
|
||||
@@ -93,22 +147,48 @@
|
||||
<h1>
|
||||
Get involved in the DiceCloud community
|
||||
</h1>
|
||||
<v-layout row wrap align-center justify-space-around class="pa-4">
|
||||
<v-btn href="https://reddit.com/r/dicecloud" flat large color="secondary">
|
||||
<v-layout
|
||||
row
|
||||
wrap
|
||||
align-center
|
||||
justify-space-around
|
||||
class="pa-4"
|
||||
>
|
||||
<v-btn
|
||||
href="https://reddit.com/r/dicecloud"
|
||||
flat
|
||||
large
|
||||
color="secondary"
|
||||
>
|
||||
Reddit
|
||||
</v-btn>
|
||||
<v-flex>
|
||||
<v-btn href="https://discord.gg/qEvdfeB" flat large color="secondary">
|
||||
<v-btn
|
||||
href="https://discord.gg/qEvdfeB"
|
||||
flat
|
||||
large
|
||||
color="secondary"
|
||||
>
|
||||
Discord
|
||||
</v-btn>
|
||||
</v-flex>
|
||||
<v-flex>
|
||||
<v-btn href="https://www.patreon.com/dicecloud" flat large color="secondary">
|
||||
<v-btn
|
||||
href="https://www.patreon.com/dicecloud"
|
||||
flat
|
||||
large
|
||||
color="secondary"
|
||||
>
|
||||
Patreon
|
||||
</v-btn>
|
||||
</v-flex>
|
||||
<v-flex>
|
||||
<v-btn href="https://github.com/ThaumRystra/DiceCloud" flat large color="secondary">
|
||||
<v-btn
|
||||
href="https://github.com/ThaumRystra/DiceCloud"
|
||||
flat
|
||||
large
|
||||
color="secondary"
|
||||
>
|
||||
Github
|
||||
</v-btn>
|
||||
</v-flex>
|
||||
@@ -119,7 +199,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ToolbarLayout from "/imports/ui/layouts/ToolbarLayout.vue";
|
||||
import ToolbarLayout from '/imports/ui/layouts/ToolbarLayout.vue';
|
||||
export default {
|
||||
components: {
|
||||
ToolbarLayout,
|
||||
|
||||
@@ -3,10 +3,22 @@
|
||||
<div slot="toolbar">
|
||||
Not Found
|
||||
</div>
|
||||
<v-layout align-center justify-center>
|
||||
<v-layout
|
||||
align-center
|
||||
justify-center
|
||||
>
|
||||
<h1>
|
||||
No page was found for this address
|
||||
</h1>
|
||||
</v-layout>
|
||||
</toolbar-layout>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ToolbarLayout from '/imports/ui/layouts/ToolbarLayout.vue';
|
||||
export default {
|
||||
components: {
|
||||
ToolbarLayout,
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
38
app/imports/ui/pages/PatreonLevelTooLow.vue
Normal file
38
app/imports/ui/pages/PatreonLevelTooLow.vue
Normal file
@@ -0,0 +1,38 @@
|
||||
<template>
|
||||
<toolbar-layout>
|
||||
<div slot="toolbar">
|
||||
Patreon tier not high enough
|
||||
</div>
|
||||
<v-layout
|
||||
column
|
||||
align-center
|
||||
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.
|
||||
</h2>
|
||||
</v-layout>
|
||||
</toolbar-layout>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ToolbarLayout from '/imports/ui/layouts/ToolbarLayout.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
ToolbarLayout,
|
||||
},
|
||||
meteor: {
|
||||
entitledDollars(){
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -1,74 +1,112 @@
|
||||
<template>
|
||||
<ToolbarLayout>
|
||||
<div slot="toolbar">Sign In</div>
|
||||
<v-form ref="form" class="mt-4">
|
||||
<v-layout column align-center>
|
||||
<v-img
|
||||
src="crown-dice-logo-cropped-transparent.png"
|
||||
width="120px"
|
||||
class="ma-3"></v-img>
|
||||
<v-text-field
|
||||
type="text"
|
||||
label="Username or email"
|
||||
v-model="name"
|
||||
:rules="nameRules"
|
||||
@keyup.enter="submit"
|
||||
required
|
||||
></v-text-field>
|
||||
<v-text-field
|
||||
type="password"
|
||||
label="Password"
|
||||
v-model="password"
|
||||
:rules="passwordRules"
|
||||
@keyup.enter="submit"
|
||||
required
|
||||
></v-text-field>
|
||||
<v-btn flat>Reset Password</v-btn>
|
||||
<div class="error--text">
|
||||
{{error}}
|
||||
</div>
|
||||
<v-layout row>
|
||||
<v-btn
|
||||
:disabled="!valid"
|
||||
@click="submit"
|
||||
color="accent"
|
||||
>
|
||||
Sign In
|
||||
</v-btn>
|
||||
<v-btn color="accent" to="/register">
|
||||
Register
|
||||
</v-btn>
|
||||
</v-layout>
|
||||
</v-layout>
|
||||
</v-form>
|
||||
<v-divider class="ma-4"></v-divider>
|
||||
<v-layout column align-center>
|
||||
<div class="error--text">
|
||||
{{googleError}}
|
||||
</div>
|
||||
<v-btn color="accent" @click="googleLogin">
|
||||
Sign in with Google
|
||||
</v-btn>
|
||||
</v-layout>
|
||||
</ToolbarLayout>
|
||||
<ToolbarLayout>
|
||||
<div slot="toolbar">
|
||||
Sign In
|
||||
</div>
|
||||
<v-form
|
||||
ref="form"
|
||||
class="mt-4"
|
||||
>
|
||||
<v-layout
|
||||
column
|
||||
align-center
|
||||
>
|
||||
<v-img
|
||||
src="crown-dice-logo-cropped-transparent.png"
|
||||
width="120px"
|
||||
class="ma-3"
|
||||
/>
|
||||
<!--
|
||||
<v-text-field
|
||||
v-model="name"
|
||||
type="text"
|
||||
label="Username or email"
|
||||
:rules="nameRules"
|
||||
required
|
||||
@keyup.enter="submit"
|
||||
/>
|
||||
<v-text-field
|
||||
v-model="password"
|
||||
type="password"
|
||||
label="Password"
|
||||
:rules="passwordRules"
|
||||
required
|
||||
@keyup.enter="submit"
|
||||
/>
|
||||
<v-btn flat>
|
||||
Reset Password
|
||||
</v-btn>
|
||||
<div class="error--text">
|
||||
{{ error }}
|
||||
</div>
|
||||
<v-layout row>
|
||||
<v-btn
|
||||
:disabled="!valid"
|
||||
color="accent"
|
||||
@click="submit"
|
||||
>
|
||||
Sign In
|
||||
</v-btn>
|
||||
<v-btn
|
||||
color="accent"
|
||||
to="/register"
|
||||
>
|
||||
Register
|
||||
</v-btn>
|
||||
</v-layout>
|
||||
-->
|
||||
</v-layout>
|
||||
</v-form>
|
||||
<v-divider class="ma-4" />
|
||||
<v-layout
|
||||
column
|
||||
align-center
|
||||
>
|
||||
<!--
|
||||
<div class="error--text">
|
||||
{{ googleError }}
|
||||
</div>
|
||||
<v-btn
|
||||
color="accent"
|
||||
@click="googleLogin"
|
||||
>
|
||||
Sign in with Google
|
||||
</v-btn>
|
||||
-->
|
||||
<div class="error--text">
|
||||
{{ patreonError }}
|
||||
</div>
|
||||
<v-btn
|
||||
color="accent"
|
||||
@click="patreonLogin"
|
||||
>
|
||||
Sign in with Patreon
|
||||
</v-btn>
|
||||
</v-layout>
|
||||
</ToolbarLayout>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ToolbarLayout from "/imports/ui/layouts/ToolbarLayout.vue";
|
||||
import router from "/imports/ui/router.js";
|
||||
import { Meteor } from 'meteor/meteor'
|
||||
import ToolbarLayout from '/imports/ui/layouts/ToolbarLayout.vue';
|
||||
import router from '/imports/ui/router.js';
|
||||
export default{
|
||||
components: {
|
||||
ToolbarLayout,
|
||||
},
|
||||
data: () => ({
|
||||
valid: true,
|
||||
name: "",
|
||||
name: '',
|
||||
nameRules: [
|
||||
v => !!v || "Name is required",
|
||||
v => !!v || 'Name is required',
|
||||
],
|
||||
password: "",
|
||||
password: '',
|
||||
passwordRules: [
|
||||
v => !!v || "Password is required",
|
||||
v => !!v || 'Password is required',
|
||||
],
|
||||
error: "",
|
||||
googleError: "",
|
||||
error: '',
|
||||
googleError: '',
|
||||
patreonError: '',
|
||||
}),
|
||||
methods: {
|
||||
submit () {
|
||||
@@ -77,19 +115,31 @@
|
||||
if (error){
|
||||
this.error = error.reason;
|
||||
} else {
|
||||
router.push("characterList");
|
||||
router.push('characterList');
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
googleLogin() {
|
||||
Meteor.loginWithGoogle(error => {
|
||||
if (error) this.googleError = error.reason;
|
||||
if (error){
|
||||
console.error(error);
|
||||
this.googleError = error.message;
|
||||
} else {
|
||||
router.push('characterList');
|
||||
}
|
||||
});
|
||||
},
|
||||
patreonLogin() {
|
||||
Meteor.loginWithPatreon(error => {
|
||||
if (error){
|
||||
console.error(error);
|
||||
this.patreonError = error.message;
|
||||
} else {
|
||||
router.push('characterList');
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
components: {
|
||||
ToolbarLayout,
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { RouterFactory, nativeScrollBehavior } from 'meteor/akryum:vue-router2';
|
||||
import Vue from 'vue';
|
||||
|
||||
// Components
|
||||
import Home from '/imports/ui/pages/Home.vue';
|
||||
@@ -10,6 +9,7 @@ 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 NotImplemented from '/imports/ui/pages/NotImplemented.vue';
|
||||
import PatreonLevelTooLow from '/imports/ui/pages/PatreonLevelTooLow.vue';
|
||||
|
||||
// Not found
|
||||
import NotFound from '/imports/ui/pages/NotFound.vue';
|
||||
@@ -21,6 +21,22 @@ const routerFactory = new RouterFactory({
|
||||
scrollBehavior: nativeScrollBehavior,
|
||||
});
|
||||
|
||||
function ensurePatronTier(to, from, next){
|
||||
let user = Meteor.user();
|
||||
if (!user){
|
||||
next('/sign-in');
|
||||
return;
|
||||
}
|
||||
let entitledCents = user.services.patreon.entitledCents || 0;
|
||||
let overrideCents = user.services.patreon.entitledCentsOverride || 0;
|
||||
|
||||
if (entitledCents < 500 && overrideCents < 500){
|
||||
next('/patreon-level-too-low');
|
||||
} else {
|
||||
next();
|
||||
}
|
||||
}
|
||||
|
||||
RouterFactory.configure(factory => {
|
||||
factory.addRoutes([
|
||||
{
|
||||
@@ -30,48 +46,36 @@ RouterFactory.configure(factory => {
|
||||
},{
|
||||
path: '/characterList',
|
||||
component: CharacterList,
|
||||
//component: NotImplemented,
|
||||
beforeEnter: ensurePatronTier,
|
||||
},{
|
||||
path: '/library',
|
||||
component: Library,
|
||||
beforeEnter: ensurePatronTier,
|
||||
},{
|
||||
path: '/character/:id/:urlName',
|
||||
component: CharacterSheetPage,
|
||||
//component: NotImplemented,
|
||||
beforeEnter: ensurePatronTier,
|
||||
},{
|
||||
path: '/character/:id',
|
||||
component: CharacterSheetPage,
|
||||
//component: NotImplemented,
|
||||
|
||||
beforeEnter: ensurePatronTier,
|
||||
},{
|
||||
path: '/sign-in',
|
||||
component: SignIn,
|
||||
},{
|
||||
},/*{
|
||||
path: '/register',
|
||||
component: Register,
|
||||
},{
|
||||
},*/{
|
||||
path: '/account',
|
||||
component: Account,
|
||||
},{
|
||||
path: '/feedback',
|
||||
component: NotImplemented,
|
||||
},{
|
||||
path: '/patreon-level-too-low',
|
||||
component: PatreonLevelTooLow,
|
||||
},
|
||||
]);
|
||||
// Storybook routes
|
||||
if (Meteor.settings.public.showStorybook || Meteor.isDevelopment){
|
||||
let StoryBook = require('/imports/ui/StoryBook.vue').default;
|
||||
factory.addRoutes([
|
||||
{
|
||||
path: '/storybook/:component',
|
||||
name: 'componentStory',
|
||||
component: StoryBook,
|
||||
},{
|
||||
path: '/storybook',
|
||||
name: 'storybook',
|
||||
component: StoryBook,
|
||||
},
|
||||
]);
|
||||
}
|
||||
// Icon admin routes
|
||||
if (Meteor.isDevelopment){
|
||||
let IconAdmin = require('/imports/ui/icons/IconAdmin.vue').default;
|
||||
|
||||
@@ -82,7 +82,8 @@
|
||||
"env": {
|
||||
"es6": true,
|
||||
"browser": true,
|
||||
"node": true
|
||||
"node": true,
|
||||
"meteor": true
|
||||
},
|
||||
"rules": {
|
||||
"quotes": [
|
||||
|
||||
1
app/packages/accounts-patreon/.gitignore
vendored
Normal file
1
app/packages/accounts-patreon/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
.build*
|
||||
17
app/packages/accounts-patreon/package.js
Normal file
17
app/packages/accounts-patreon/package.js
Normal file
@@ -0,0 +1,17 @@
|
||||
Package.describe({
|
||||
summary: 'Login service for Patreon accounts',
|
||||
version: '0.1.0',
|
||||
});
|
||||
|
||||
Package.onUse(api => {
|
||||
api.use('ecmascript');
|
||||
api.use('accounts-base', ['client', 'server']);
|
||||
// Export Accounts (etc) to packages using this one.
|
||||
api.imply('accounts-base', ['client', 'server']);
|
||||
|
||||
api.use('accounts-oauth', ['client', 'server']);
|
||||
api.use('patreon-oauth');
|
||||
api.imply('patreon-oauth');
|
||||
|
||||
api.addFiles('patreon.js');
|
||||
});
|
||||
26
app/packages/accounts-patreon/patreon.js
Normal file
26
app/packages/accounts-patreon/patreon.js
Normal file
@@ -0,0 +1,26 @@
|
||||
Accounts.oauth.registerService('patreon');
|
||||
console.log('accounts-patreon');
|
||||
|
||||
if (Meteor.isClient) {
|
||||
const loginWithPatreon = (options, callback) => {
|
||||
// support a callback without options
|
||||
if (! callback && typeof options === 'function') {
|
||||
callback = options;
|
||||
options = null;
|
||||
}
|
||||
|
||||
const credentialRequestCompleteCallback = Accounts.oauth.credentialRequestCompleteHandler(callback);
|
||||
Patreon.requestCredential(options, credentialRequestCompleteCallback);
|
||||
};
|
||||
Accounts.registerClientLoginFunction('patreon', loginWithPatreon);
|
||||
Meteor.loginWithPatreon =
|
||||
(...args) => Accounts.applyLoginFunction('patreon', args);
|
||||
} else {
|
||||
Accounts.addAutopublishFields({
|
||||
// publish all fields including access token, which can legitimately
|
||||
// be used from the client (if transmitted over ssl or on
|
||||
// localhost). http://www.meetup.com/meetup_api/auth/#oauth2implicit
|
||||
forLoggedInUser: ['services.patreon'],
|
||||
forOtherUsers: ['services.patreon.id']
|
||||
});
|
||||
}
|
||||
3
app/packages/patreon-oauth/README.md
Normal file
3
app/packages/patreon-oauth/README.md
Normal file
@@ -0,0 +1,3 @@
|
||||
# patreon-oauth
|
||||
|
||||
An implementation of the Patreon OAuth flow. See the [Meteor Guide](https://guide.meteor.com/accounts.html) for more details.
|
||||
18
app/packages/patreon-oauth/package.js
Normal file
18
app/packages/patreon-oauth/package.js
Normal file
@@ -0,0 +1,18 @@
|
||||
Package.describe({
|
||||
summary: 'Patreon OAuth flow',
|
||||
version: '0.1.0'
|
||||
});
|
||||
|
||||
Package.onUse(api => {
|
||||
api.use('ecmascript');
|
||||
api.use('oauth2', ['client', 'server']);
|
||||
api.use('oauth', ['client', 'server']);
|
||||
api.use('http', 'server');
|
||||
api.use('random', 'client');
|
||||
api.use('service-configuration', ['client', 'server']);
|
||||
|
||||
api.addFiles('patreon_server.js', 'server');
|
||||
api.addFiles('patreon_client.js', 'client');
|
||||
|
||||
api.export('Patreon');
|
||||
});
|
||||
54
app/packages/patreon-oauth/patreon_client.js
Normal file
54
app/packages/patreon-oauth/patreon_client.js
Normal file
@@ -0,0 +1,54 @@
|
||||
Patreon = {};
|
||||
console.log('patreon-oauth');
|
||||
|
||||
// Request Patreon credentials for the user
|
||||
// @param options {optional}
|
||||
// @param credentialRequestCompleteCallback {Function} Callback function to call on
|
||||
// completion. Takes one argument, credentialToken on success, or Error on
|
||||
// error.
|
||||
Patreon.requestCredential = (options, credentialRequestCompleteCallback) => {
|
||||
// support both (options, callback) and (callback).
|
||||
if (!credentialRequestCompleteCallback && typeof options === 'function') {
|
||||
credentialRequestCompleteCallback = options;
|
||||
options = {};
|
||||
}
|
||||
|
||||
const config = ServiceConfiguration.configurations.findOne({service: 'patreon'});
|
||||
if (!config) {
|
||||
credentialRequestCompleteCallback && credentialRequestCompleteCallback(
|
||||
new ServiceConfiguration.ConfigError());
|
||||
return;
|
||||
}
|
||||
|
||||
// For some reason, meetup converts underscores to spaces in the state
|
||||
// parameter when redirecting back to the client, so we use
|
||||
// `Random.id()` here (alphanumerics) instead of `Random.secret()`
|
||||
// (base 64 characters).
|
||||
const credentialToken = Random.id();
|
||||
|
||||
const scope = (options && options.requestPermissions) || [
|
||||
'identity',
|
||||
'identity[email]',
|
||||
];
|
||||
const flatScope = scope.map(encodeURIComponent).join(' ');
|
||||
//const flatScope = encodeURIComponent(scope.join(','));
|
||||
|
||||
console.log({flatScope})
|
||||
const loginStyle = OAuth._loginStyle('patreon', config, options);
|
||||
|
||||
const loginUrl =
|
||||
'https://www.patreon.com/oauth2/authorize' +
|
||||
`?client_id=${config.clientId}` +
|
||||
'&response_type=code' +
|
||||
(flatScope ? `&scope=${flatScope}` : '') +
|
||||
`&redirect_uri=${OAuth._redirectUri('patreon', config)}` +
|
||||
`&state=${OAuth._stateParam(loginStyle, credentialToken, options && options.redirectUrl)}`;
|
||||
|
||||
OAuth.launchLogin({
|
||||
loginService: 'patreon',
|
||||
loginStyle,
|
||||
loginUrl,
|
||||
credentialRequestCompleteCallback,
|
||||
credentialToken,
|
||||
});
|
||||
};
|
||||
75
app/packages/patreon-oauth/patreon_server.js
Normal file
75
app/packages/patreon-oauth/patreon_server.js
Normal file
@@ -0,0 +1,75 @@
|
||||
Patreon = {};
|
||||
|
||||
OAuth.registerService('patreon', 2, null, query => {
|
||||
const response = getAccessToken(query);
|
||||
const accessToken = response.access_token;
|
||||
const refreshToken = response.refresh_token;
|
||||
const scope = response.scope;
|
||||
const expiresAt = (+new Date) + (1000 * response.expires_in);
|
||||
const identity = getIdentity(accessToken);
|
||||
let serviceData = {
|
||||
id : identity.data.id,
|
||||
email: identity.data.attributes.email,
|
||||
entitledCents: identity.included[0] &&
|
||||
identity.included[0].attributes.currently_entitled_amount_cents || 0,
|
||||
accessToken,
|
||||
refreshToken,
|
||||
scope,
|
||||
expiresAt,
|
||||
};
|
||||
return { serviceData };
|
||||
});
|
||||
|
||||
const getAccessToken = query => {
|
||||
const config = ServiceConfiguration.configurations.findOne({service: 'patreon'});
|
||||
if (!config)
|
||||
throw new ServiceConfiguration.ConfigError();
|
||||
|
||||
let response;
|
||||
try {
|
||||
response = HTTP.post(
|
||||
'https://www.patreon.com/api/oauth2/token', {headers: {Accept: 'application/json'}, params: {
|
||||
code: query.code,
|
||||
client_id: config.clientId,
|
||||
client_secret: config.secret,
|
||||
grant_type: 'authorization_code',
|
||||
redirect_uri: OAuth._redirectUri('patreon', config),
|
||||
}});
|
||||
} catch (err) {
|
||||
throw Object.assign(
|
||||
new Error(`Failed to complete OAuth handshake with Patreon. ${err.message}`),
|
||||
{ response: err.response }
|
||||
);
|
||||
}
|
||||
|
||||
if (response.data.error) { // if the http response was a json object with an error attribute
|
||||
throw new Error(`Failed to complete OAuth handshake with Patreon. ${response.data.error}`);
|
||||
} else {
|
||||
return response.data;
|
||||
}
|
||||
};
|
||||
|
||||
const getIdentity = accessToken => {
|
||||
try {
|
||||
const response = HTTP.get(
|
||||
'https://www.patreon.com/api/oauth2/v2/identity?' +
|
||||
'fields%5Buser%5D=email&' +
|
||||
'fields%5Bmember%5D=currently_entitled_amount_cents&' +
|
||||
'include=memberships',
|
||||
{
|
||||
headers: {authorization: `Bearer ${accessToken}`},
|
||||
}
|
||||
);
|
||||
let data = JSON.parse(response.content);
|
||||
return data;
|
||||
} catch (err) {
|
||||
throw Object.assign(
|
||||
new Error(`Failed to fetch identity from Patreon. ${err.message}`),
|
||||
{ response: err.response }
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Patreon.retrieveCredential = (credentialToken, credentialSecret) =>
|
||||
OAuth.retrieveCredential(credentialToken, credentialSecret);
|
||||
Reference in New Issue
Block a user