From 27d763a46cb5546193b2b3bc5247e8dd0ddfd0ba Mon Sep 17 00:00:00 2001 From: negue Date: Sat, 2 Jun 2018 18:14:53 +0200 Subject: [PATCH 01/45] add slack debug calls (logger.info) --- website/server/libs/slack.js | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/website/server/libs/slack.js b/website/server/libs/slack.js index 44dbd9e95b..fe3b9696e5 100644 --- a/website/server/libs/slack.js +++ b/website/server/libs/slack.js @@ -8,6 +8,10 @@ const SLACK_FLAGGING_URL = nconf.get('SLACK:FLAGGING_URL'); const SLACK_FLAGGING_FOOTER_LINK = nconf.get('SLACK:FLAGGING_FOOTER_LINK'); const SLACK_SUBSCRIPTIONS_URL = nconf.get('SLACK:SUBSCRIPTIONS_URL'); const BASE_URL = nconf.get('BASE_URL'); +const IS_PRODUCTION = nconf.get('IS_PROD'); + +const SKIP_FLAG_METHODS = IS_PRODUCTION && !SLACK_FLAGGING_URL; +const SKIP_SUB_METHOD = IS_PRODUCTION && !SLACK_SUBSCRIPTIONS_URL; let flagSlack; let subscriptionSlack; @@ -17,6 +21,14 @@ try { subscriptionSlack = new IncomingWebhook(SLACK_SUBSCRIPTIONS_URL); } catch (err) { logger.error(err); + + if (!IS_PRODUCTION) { + flagSlack = subscriptionSlack = { + send (data) { + logger.info('Data sent to slack', data); + }, + }; + } } function sendFlagNotification ({ @@ -26,7 +38,7 @@ function sendFlagNotification ({ message, userComment, }) { - if (!SLACK_FLAGGING_URL) { + if (SKIP_FLAG_METHODS) { return; } let titleLink; @@ -76,7 +88,7 @@ function sendSubscriptionNotification ({ months, groupId, }) { - if (!SLACK_SUBSCRIPTIONS_URL) { + if (SKIP_SUB_METHOD) { return; } let text; @@ -94,18 +106,13 @@ function sendSubscriptionNotification ({ }); } -module.exports = { - sendFlagNotification, - sendSubscriptionNotification, -}; - function sendSlurNotification ({ authorEmail, author, group, message, }) { - if (!SLACK_FLAGGING_URL) { + if (SKIP_FLAG_METHODS) { return; } let titleLink; @@ -142,5 +149,7 @@ function sendSlurNotification ({ } module.exports = { - sendFlagNotification, sendSubscriptionNotification, sendSlurNotification, + sendFlagNotification, + sendSubscriptionNotification, + sendSlurNotification, }; From 532881e679fb2680394a61254fd4256dc6ede9f1 Mon Sep 17 00:00:00 2001 From: negue Date: Sat, 2 Jun 2018 18:16:24 +0200 Subject: [PATCH 02/45] enable flagging private message - v-once for simple strings/icons - fix flag notification --- website/client/components/chat/chatCard.vue | 11 +++++------ website/client/components/chat/reportFlagModal.vue | 2 +- website/client/store/actions/chat.js | 9 ++++++++- 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/website/client/components/chat/chatCard.vue b/website/client/components/chat/chatCard.vue index be680634ce..e2db8411b1 100644 --- a/website/client/components/chat/chatCard.vue +++ b/website/client/components/chat/chatCard.vue @@ -19,14 +19,13 @@ div span(v-if='!msg.likes[user._id]') {{ $t('like') }} span(v-if='msg.likes[user._id]') {{ $t('liked') }} span.action(v-if='!inbox', @click='copyAsTodo(msg)') - .svg-icon(v-html="icons.copy") + .svg-icon(v-html="icons.copy", v-once) | {{$t('copyAsTodo')}} - span.action(v-if='!inbox && user.flags.communityGuidelinesAccepted && msg.uuid !== "system"', @click='report(msg)') - .svg-icon(v-html="icons.report") + span.action(v-if='inbox || (user.flags.communityGuidelinesAccepted && msg.uuid !== "system")', @click='report(msg)') + .svg-icon(v-html="icons.report", v-once) | {{$t('report')}} - // @TODO make flagging/reporting work in the inbox. NOTE: it must work even if the communityGuidelines are not accepted and it MUST work for messages that you have SENT as well as received. -- Alys span.action(v-if='msg.uuid === user._id || inbox || user.contributor.admin', @click='remove()') - .svg-icon(v-html="icons.delete") + .svg-icon(v-html="icons.delete", v-once) | {{$t('delete')}} span.action.float-right.liked(v-if='likeCount > 0') .svg-icon(v-html="icons.liked") @@ -243,7 +242,7 @@ export default { async report () { this.$root.$emit('habitica::report-chat', { message: this.msg, - groupId: this.groupId, + groupId: this.groupId || 'privateMessage', }); }, async remove () { diff --git a/website/client/components/chat/reportFlagModal.vue b/website/client/components/chat/reportFlagModal.vue index 70aefdb40d..9d2e177987 100644 --- a/website/client/components/chat/reportFlagModal.vue +++ b/website/client/components/chat/reportFlagModal.vue @@ -111,7 +111,7 @@ export default { this.$root.$emit('bv::hide::modal', 'report-flag'); }, async reportAbuse () { - this.notify('Thank you for reporting this violation. The moderators have been notified.'); + this.text('Thank you for reporting this violation. The moderators have been notified.'); await this.$store.dispatch('chat:flag', { groupId: this.groupId, diff --git a/website/client/store/actions/chat.js b/website/client/store/actions/chat.js index 5bb5840937..509f6b33cf 100644 --- a/website/client/store/actions/chat.js +++ b/website/client/store/actions/chat.js @@ -69,7 +69,14 @@ export async function like (store, payload) { } export async function flag (store, payload) { - const url = `/api/v3/groups/${payload.groupId}/chat/${payload.chatId}/flag`; + let url = ''; + + if (payload.groupId === 'privateMessage') { + url = `/api/v3/members/flag-private-message/${payload.chatId}`; + } else { + url = `/api/v3/groups/${payload.groupId}/chat/${payload.chatId}/flag`; + } + const response = await axios.post(url, { comment: payload.comment, }); From 5a0eed7eae349a4db3fa376cd84aa88c26ab6339 Mon Sep 17 00:00:00 2001 From: negue Date: Sat, 2 Jun 2018 18:18:00 +0200 Subject: [PATCH 03/45] copy chatReporter - fix unknown apiMessages - add api for flagging pm --- website/common/errors/apiErrorMessages.js | 3 + website/server/controllers/api-v3/chat.js | 6 +- website/server/controllers/api-v3/members.js | 35 +++++++ .../libs/chatReporting/chatReporterFactory.js | 7 +- .../libs/chatReporting/groupChatReporter.js | 2 +- .../libs/chatReporting/inboxChatReporter.js | 95 +++++++++++++++++++ 6 files changed, 140 insertions(+), 8 deletions(-) create mode 100644 website/server/libs/chatReporting/inboxChatReporter.js diff --git a/website/common/errors/apiErrorMessages.js b/website/common/errors/apiErrorMessages.js index 4857be3d58..5690cdfe9d 100644 --- a/website/common/errors/apiErrorMessages.js +++ b/website/common/errors/apiErrorMessages.js @@ -8,6 +8,9 @@ module.exports = { missingTypeKeyEquip: '"key" and "type" are required parameters.', + chatIdRequired: 'req.params.chatId must contain a chatId.', + messageIdRequired: 'req.params.messageId must contain a message ID.', + guildsOnlyPaginate: 'Only public guilds support pagination.', guildsPaginateBooleanString: 'req.query.paginate must be a boolean string.', groupIdRequired: 'req.params.groupId must contain a groupId.', diff --git a/website/server/controllers/api-v3/chat.js b/website/server/controllers/api-v3/chat.js index 34a3868efe..a8f5241971 100644 --- a/website/server/controllers/api-v3/chat.js +++ b/website/server/controllers/api-v3/chat.js @@ -234,7 +234,7 @@ api.likeChat = { let groupId = req.params.groupId; req.checkParams('groupId', apiError('groupIdRequired')).notEmpty(); - req.checkParams('chatId', res.t('chatIdRequired')).notEmpty(); + req.checkParams('chatId', apiError('chatIdRequired')).notEmpty(); let validationErrors = req.validationErrors(); if (validationErrors) throw validationErrors; @@ -325,7 +325,7 @@ api.clearChatFlags = { let chatId = req.params.chatId; req.checkParams('groupId', apiError('groupIdRequired')).notEmpty(); - req.checkParams('chatId', res.t('chatIdRequired')).notEmpty(); + req.checkParams('chatId', apiError('chatIdRequired')).notEmpty(); let validationErrors = req.validationErrors(); if (validationErrors) throw validationErrors; @@ -465,7 +465,7 @@ api.deleteChat = { let chatId = req.params.chatId; req.checkParams('groupId', apiError('groupIdRequired')).notEmpty(); - req.checkParams('chatId', res.t('chatIdRequired')).notEmpty(); + req.checkParams('chatId', apiError('chatIdRequired')).notEmpty(); let validationErrors = req.validationErrors(); if (validationErrors) throw validationErrors; diff --git a/website/server/controllers/api-v3/members.js b/website/server/controllers/api-v3/members.js index 421e2cf4bb..151e64bc2d 100644 --- a/website/server/controllers/api-v3/members.js +++ b/website/server/controllers/api-v3/members.js @@ -20,6 +20,7 @@ import { } from '../../libs/email'; import { sendNotification as sendPushNotification } from '../../libs/pushNotifications'; import { achievements } from '../../../../website/common/'; +import {chatReporterFactory} from '../../libs/chatReporting/chatReporterFactory'; let api = {}; @@ -514,6 +515,40 @@ api.sendPrivateMessage = { }, }; + +/** + * @api {post} /api/v3//members/flag-private-message/:messageId Flag a private message + * @apiDescription A message will be hidden immediately if a moderator flags the message. An email is sent to the moderators about every flagged message. + * @apiName FlagPrivateMessage + * @apiGroup Member + * + * @apiParam (Path) {UUID} messageId The private message id + * + * @apiSuccess {Object} data The flagged chat message + * @apiSuccess {UUID} data.id The id of the message + * @apiSuccess {String} data.text The text of the message + * @apiSuccess {Number} data.timestamp The timestamp of the message in milliseconds + * @apiSuccess {Object} data.likes The likes of the message + * @apiSuccess {Object} data.flags The flags of the message + * @apiSuccess {Number} data.flagCount The number of flags the message has + * @apiSuccess {UUID} data.uuid The user id of the author of the message + * @apiSuccess {String} data.user The username of the author of the message + * + * @apiUse MessageNotFound + * @apiUse MessageIdRequired + * @apiError (404) {NotFound} messageGroupChatFlagAlreadyReported The message has already been flagged + */ +api.flagPrivateMessage = { + method: 'POST', + url: '/members/flag-private-message/:messageId', + middlewares: [authWithHeaders()], + async handler (req, res) { + const chatReporter = chatReporterFactory('Inbox', req, res); + const message = await chatReporter.flag(); + res.respond(200, message); + }, +}; + /** * @api {post} /api/v3/members/transfer-gems Send a gem gift to a member * @apiName TransferGems diff --git a/website/server/libs/chatReporting/chatReporterFactory.js b/website/server/libs/chatReporting/chatReporterFactory.js index cd97da6793..50ac7e9e89 100644 --- a/website/server/libs/chatReporting/chatReporterFactory.js +++ b/website/server/libs/chatReporting/chatReporterFactory.js @@ -1,11 +1,10 @@ import GroupChatReporter from './groupChatReporter'; -// import InboxChatReporter from './inboxChatReporter'; +import InboxChatReporter from './inboxChatReporter'; export function chatReporterFactory (type, req, res) { if (type === 'Group') { return new GroupChatReporter(req, res); + } else if (type === 'Inbox') { + return new InboxChatReporter(req, res); } - // else if (type === 'Inbox') { - // return new InboxChatReporter(req, res); - // } } diff --git a/website/server/libs/chatReporting/groupChatReporter.js b/website/server/libs/chatReporting/groupChatReporter.js index c344607418..658e51b335 100644 --- a/website/server/libs/chatReporting/groupChatReporter.js +++ b/website/server/libs/chatReporting/groupChatReporter.js @@ -26,7 +26,7 @@ export default class GroupChatReporter extends ChatReporter { async validate () { this.req.checkParams('groupId', apiError('groupIdRequired')).notEmpty(); - this.req.checkParams('chatId', this.res.t('chatIdRequired')).notEmpty(); + this.req.checkParams('chatId', apiError('chatIdRequired')).notEmpty(); let validationErrors = this.req.validationErrors(); if (validationErrors) throw validationErrors; diff --git a/website/server/libs/chatReporting/inboxChatReporter.js b/website/server/libs/chatReporting/inboxChatReporter.js new file mode 100644 index 0000000000..841daa25d7 --- /dev/null +++ b/website/server/libs/chatReporting/inboxChatReporter.js @@ -0,0 +1,95 @@ +import nconf from 'nconf'; + +import ChatReporter from './chatReporter'; +import { + BadRequest, + NotFound, +} from '../errors'; +import { getGroupUrl, sendTxn } from '../email'; +import slack from '../slack'; +import { model as Group } from '../../models/group'; +import { model as Chat } from '../../models/chat'; +import apiError from '../apiError'; + +const COMMUNITY_MANAGER_EMAIL = nconf.get('EMAILS:COMMUNITY_MANAGER_EMAIL'); +const FLAG_REPORT_EMAILS = nconf.get('FLAG_REPORT_EMAIL').split(',').map((email) => { + return { email, canSend: true }; +}); + +export default class InboxChatReporter extends ChatReporter { + constructor (req, res) { + super(req, res); + + this.user = res.locals.user; + } + + async validate () { + this.req.checkParams('messageId', apiError('messageIdRequired')).notEmpty(); + + let validationErrors = this.req.validationErrors(); + if (validationErrors) throw validationErrors; + + let group = await Group.getGroup({ + user: this.user, + groupId: this.groupId, + optionalMembership: this.user.contributor.admin, + }); + if (!group) throw new NotFound(this.res.t('groupNotFound')); + + const message = await Chat.findOne({_id: this.req.params.chatId}).exec(); + if (!message) throw new NotFound(this.res.t('messageGroupChatNotFound')); + if (message.uuid === 'system') throw new BadRequest(this.res.t('messageCannotFlagSystemMessages', {communityManagerEmail: COMMUNITY_MANAGER_EMAIL})); + + const userComment = this.req.body.comment; + + return {message, group, userComment}; + } + + async notify (group, message, userComment) { + await super.notify(group, message); + + const groupUrl = getGroupUrl(group); + sendTxn(FLAG_REPORT_EMAILS, 'flag-report-to-mods-with-comments', this.emailVariables.concat([ + {name: 'GROUP_NAME', content: group.name}, + {name: 'GROUP_TYPE', content: group.type}, + {name: 'GROUP_ID', content: group._id}, + {name: 'GROUP_URL', content: groupUrl}, + {name: 'REPORTER_COMMENT', content: userComment || ''}, + ])); + + slack.sendFlagNotification({ + authorEmail: this.authorEmail, + flagger: this.user, + group, + message, + userComment, + }); + } + + async flagGroupMessage (group, message) { + // Log user ids that have flagged the message + if (!message.flags) message.flags = {}; + // TODO fix error type + if (message.flags[this.user._id] && !this.user.contributor.admin) throw new NotFound(this.res.t('messageGroupChatFlagAlreadyReported')); + message.flags[this.user._id] = true; + message.markModified('flags'); + + // Log total number of flags (publicly viewable) + if (!message.flagCount) message.flagCount = 0; + if (this.user.contributor.admin) { + // Arbitrary amount, higher than 2 + message.flagCount = 5; + } else { + message.flagCount++; + } + + await message.save(); + } + + async flag () { + let {message, group, userComment} = await this.validate(); + await this.flagGroupMessage(group, message); + await this.notify(group, message, userComment); + return message; + } +} From ca5927fe733481e5518707a513445d4f2929846c Mon Sep 17 00:00:00 2001 From: negue Date: Sat, 2 Jun 2018 19:27:28 +0200 Subject: [PATCH 04/45] sendInboxFlagNotification (for private message flag content) --- .../libs/chatReporting/inboxChatReporter.js | 41 ++++++------ website/server/libs/slack.js | 63 ++++++++++++++++++- 2 files changed, 81 insertions(+), 23 deletions(-) diff --git a/website/server/libs/chatReporting/inboxChatReporter.js b/website/server/libs/chatReporting/inboxChatReporter.js index 841daa25d7..e78ba8c0ad 100644 --- a/website/server/libs/chatReporting/inboxChatReporter.js +++ b/website/server/libs/chatReporting/inboxChatReporter.js @@ -2,16 +2,14 @@ import nconf from 'nconf'; import ChatReporter from './chatReporter'; import { - BadRequest, NotFound, } from '../errors'; import { getGroupUrl, sendTxn } from '../email'; import slack from '../slack'; -import { model as Group } from '../../models/group'; -import { model as Chat } from '../../models/chat'; import apiError from '../apiError'; -const COMMUNITY_MANAGER_EMAIL = nconf.get('EMAILS:COMMUNITY_MANAGER_EMAIL'); +import _find from 'lodash/find'; + const FLAG_REPORT_EMAILS = nconf.get('FLAG_REPORT_EMAIL').split(',').map((email) => { return { email, canSend: true }; }); @@ -29,23 +27,21 @@ export default class InboxChatReporter extends ChatReporter { let validationErrors = this.req.validationErrors(); if (validationErrors) throw validationErrors; - let group = await Group.getGroup({ - user: this.user, - groupId: this.groupId, - optionalMembership: this.user.contributor.admin, - }); - if (!group) throw new NotFound(this.res.t('groupNotFound')); + let messages = this.user.inbox.messages; - const message = await Chat.findOne({_id: this.req.params.chatId}).exec(); + const message = _find(messages, (m) => m.id === this.req.params.messageId); if (!message) throw new NotFound(this.res.t('messageGroupChatNotFound')); - if (message.uuid === 'system') throw new BadRequest(this.res.t('messageCannotFlagSystemMessages', {communityManagerEmail: COMMUNITY_MANAGER_EMAIL})); const userComment = this.req.body.comment; - return {message, group, userComment}; + return {message, userComment}; } - async notify (group, message, userComment) { + async notify (message, userComment) { + const group = { + type: 'private messages', + }; + await super.notify(group, message); const groupUrl = getGroupUrl(group); @@ -57,22 +53,20 @@ export default class InboxChatReporter extends ChatReporter { {name: 'REPORTER_COMMENT', content: userComment || ''}, ])); - slack.sendFlagNotification({ + slack.sendInboxFlagNotification({ authorEmail: this.authorEmail, flagger: this.user, - group, message, userComment, }); } - async flagGroupMessage (group, message) { + async flagInboxMessage (message) { // Log user ids that have flagged the message if (!message.flags) message.flags = {}; // TODO fix error type if (message.flags[this.user._id] && !this.user.contributor.admin) throw new NotFound(this.res.t('messageGroupChatFlagAlreadyReported')); message.flags[this.user._id] = true; - message.markModified('flags'); // Log total number of flags (publicly viewable) if (!message.flagCount) message.flagCount = 0; @@ -83,13 +77,16 @@ export default class InboxChatReporter extends ChatReporter { message.flagCount++; } - await message.save(); + this.user.inbox.messages[message.id] = message; + this.user.markModified('inbox.messages'); + + await this.user.save(); } async flag () { - let {message, group, userComment} = await this.validate(); - await this.flagGroupMessage(group, message); - await this.notify(group, message, userComment); + let {message, userComment} = await this.validate(); + await this.flagInboxMessage(message); + await this.notify(message, userComment); return message; } } diff --git a/website/server/libs/slack.js b/website/server/libs/slack.js index fe3b9696e5..0d4060ce38 100644 --- a/website/server/libs/slack.js +++ b/website/server/libs/slack.js @@ -44,7 +44,7 @@ function sendFlagNotification ({ let titleLink; let authorName; let title = `Flag in ${group.name}`; - let text = `${flagger.profile.name} (${flagger.id}; language: ${flagger.preferences.language}) flagged a message`; + let text = `${flagger.profile.name} (${flagger.id}; language: ${flagger.preferences.language}) flagged a group message`; if (userComment) { text += ` and commented: ${userComment}`; @@ -81,6 +81,66 @@ function sendFlagNotification ({ }); } +function sendInboxFlagNotification ({ + authorEmail, + flagger, + message, + userComment, +}) { + if (SKIP_FLAG_METHODS) { + return; + } + let titleLink; + let authorName; + let title = `Flag in ${flagger.profile.name}'s Inbox`; + let text = `${flagger.profile.name} (${flagger.id}; language: ${flagger.preferences.language}) flagged a PM`; + + if (userComment) { + text += ` and commented: ${userComment}`; + } + + if (!message.user && message.uuid === 'system') { + authorName = 'System Message'; + } else { + authorName = `${message.user} - ${authorEmail} - ${message.uuid}`; + } + + let messageText = message.text; + + if (flagger.id === message.uuid) { + messageText += `: ${flagger.profile.name} is writing to itself.`; + } else { + let sender = ''; + let recipient = ''; + + if (message.sent) { + sender = flagger.profile.name; + recipient = message.user; + } else { + sender = message.user; + recipient = flagger.profile.name; + } + + messageText += `: ${sender} is writing this message to ${recipient}.`; + } + + flagSlack.send({ + text, + attachments: [{ + fallback: 'Flag Message', + color: 'danger', + author_name: authorName, + title, + title_link: titleLink, + text: messageText, + footer: `<${SLACK_FLAGGING_FOOTER_LINK}?groupId=privateMessages&chatId=${message.id}|Flag this message>`, + mrkdwn_in: [ + 'text', + ], + }], + }); +} + function sendSubscriptionNotification ({ buyer, recipient, @@ -150,6 +210,7 @@ function sendSlurNotification ({ module.exports = { sendFlagNotification, + sendInboxFlagNotification, sendSubscriptionNotification, sendSlurNotification, }; From e3b2443029b023ff69a57037103172fb2d2c00c4 Mon Sep 17 00:00:00 2001 From: negue Date: Fri, 8 Jun 2018 19:15:21 +0200 Subject: [PATCH 05/45] move reportFlagModal to menu.vue --- .../client/components/chat/chatMessages.vue | 3 - website/client/components/header/menu.vue | 4 + website/client/files.txt | 329 ++++++++++++++++++ 3 files changed, 333 insertions(+), 3 deletions(-) create mode 100644 website/client/files.txt diff --git a/website/client/components/chat/chatMessages.vue b/website/client/components/chat/chatMessages.vue index d5b97b37f8..fe3597d513 100644 --- a/website/client/components/chat/chatMessages.vue +++ b/website/client/components/chat/chatMessages.vue @@ -3,7 +3,6 @@ .row .col-12 copy-as-todo-modal(:group-name='groupName', :group-id='groupId') - report-flag-modal div(v-for="(msg, index) in messages", v-if='chat && canViewFlag(msg)') // @TODO: is there a different way to do these conditionals? This creates an infinite loop //.hr(v-if='displayDivider(msg)') @@ -83,14 +82,12 @@ import findIndex from 'lodash/findIndex'; import Avatar from '../avatar'; import copyAsTodoModal from './copyAsTodoModal'; -import reportFlagModal from './reportFlagModal'; import chatCard from './chatCard'; export default { props: ['chat', 'groupId', 'groupName', 'inbox'], components: { copyAsTodoModal, - reportFlagModal, chatCard, Avatar, }, diff --git a/website/client/components/header/menu.vue b/website/client/components/header/menu.vue index 7e72975a0e..782a3c1b99 100644 --- a/website/client/components/header/menu.vue +++ b/website/client/components/header/menu.vue @@ -3,6 +3,7 @@ div inbox-modal creator-intro profile + report-flag-modal b-navbar.topbar.navbar-inverse.static-top.navbar-expand-lg(type="dark", :class="navbarZIndexClass") b-navbar-brand.brand .logo.svg-icon.d-none.d-xl-block(v-html="icons.logo") @@ -349,6 +350,8 @@ import creatorIntro from '../creatorIntro'; import profile from '../userMenu/profile'; import userDropdown from './userDropdown'; +import reportFlagModal from '../chat/reportFlagModal'; + export default { components: { userDropdown, @@ -356,6 +359,7 @@ export default { notificationMenu, creatorIntro, profile, + reportFlagModal, }, data () { return { diff --git a/website/client/files.txt b/website/client/files.txt new file mode 100644 index 0000000000..9d0f9846d3 --- /dev/null +++ b/website/client/files.txt @@ -0,0 +1,329 @@ + 100 files 200 files 300 files 400 files 461 text files. +classified 461 files Duplicate file check 461 files (431 known unique) Unique: 100 files Unique: 200 files Unique: 300 files Unique: 400 files 460 unique files. +Counting: 88 Counting: 230 146 files ignored. + +github.com/AlDanial/cloc v 1.76 T=1.30 s (245.4 files/s, 69229.8 lines/s) +--------------------------------------------------------------------------------------------------------------- +File blank comment code +--------------------------------------------------------------------------------------------------------------- +./assets/css/sprites/spritesmith-main-3.css 0 0 3948 +./assets/css/sprites/spritesmith-main-4.css 0 0 3948 +./assets/css/sprites/spritesmith-main-2.css 0 0 3948 +./assets/css/sprites/spritesmith-main-1.css 0 0 3258 +./assets/css/sprites/spritesmith-main-5.css 0 0 3096 +./assets/css/sprites/spritesmith-main-9.css 0 0 2622 +./assets/css/sprites/spritesmith-main-7.css 0 0 2334 +./assets/css/sprites/spritesmith-main-8.css 0 0 2298 +./assets/css/sprites/spritesmith-main-6.css 0 0 2166 +./assets/css/sprites/spritesmith-main-11.css 0 0 2082 +./assets/css/sprites/spritesmith-main-19.css 0 0 2004 +./assets/css/sprites/spritesmith-main-20.css 0 0 1992 +./assets/css/sprites/spritesmith-main-18.css 0 0 1992 +./assets/css/sprites/spritesmith-main-17.css 0 0 1590 +./assets/css/sprites/spritesmith-main-21.css 0 0 1578 +./assets/css/sprites/spritesmith-main-15.css 0 0 1470 +./assets/css/sprites/spritesmith-main-12.css 0 0 1458 +./assets/css/sprites/spritesmith-main-13.css 0 0 1440 +./assets/css/sprites/spritesmith-main-16.css 0 0 1440 +./assets/css/sprites/spritesmith-main-14.css 0 0 1344 +./components/creatorIntro.vue 137 67 1329 +./assets/css/sprites/spritesmith-main-0.css 0 0 1110 +./components/tasks/taskModal.vue 98 8 869 +./components/inventory/stable/index.vue 125 15 839 +./components/groups/tavern.vue 82 21 708 +./components/tasks/task.vue 100 17 668 +./components/shops/market/index.vue 92 7 653 +./components/static/home.vue 89 12 577 +./components/static/terms.vue 0 0 568 +./components/groups/group.vue 57 35 566 +./components/tasks/column.vue 73 34 558 +./components/notifications.vue 58 32 483 +./app.vue 85 50 474 +./components/userMenu/profile.vue 58 30 469 +./components/tasks/user.vue 51 4 449 +./components/userMenu/profileStats.vue 48 3 448 +./components/shops/seasonal/index.vue 84 4 448 +./components/groups/membersModal.vue 40 8 432 +./components/auth/registerLoginReset.vue 64 33 425 +./components/shops/quests/index.vue 72 0 424 +./assets/css/sprites/spritesmith-main-10.css 0 0 420 +./components/inventory/items/index.vue 62 6 420 +./components/challenges/challengeModal.vue 41 10 418 +./components/group-plans/taskInformation.vue 42 5 410 +./components/shops/buyModal.vue 67 2 407 +./components/groups/groupFormModal.vue 49 27 383 +./components/payments/buyGemsModal.vue 36 3 368 +./components/groups/groupPlan.vue 48 4 358 +./components/inventory/equipment/index.vue 36 4 350 +./components/challenges/challengeDetail.vue 26 20 346 +./components/header/menu.vue 43 5 337 +./components/settings/site.vue 32 25 334 +./components/appFooter.vue 33 26 321 +./components/shops/timeTravelers/index.vue 67 2 318 +./components/static/privacy.vue 0 0 299 +./components/userMenu/inbox.vue 47 12 294 +./assets/scss/task.scss 32 0 292 +./components/shops/quests/buyQuestModal.vue 44 0 292 +./router.js 32 38 288 +./components/userMenu/stats.vue 19 0 282 +./components/modifyInventory.vue 44 2 270 +./components/groups/questSidebarSection.vue 28 5 249 +./components/memberDetails.vue 26 4 247 +./components/challenges/challengeItem.vue 31 2 245 +./components/settings/subscription.vue 16 5 244 +./components/payments/amazonModal.vue 29 13 241 +./components/chat/chatCard.vue 29 3 239 +./components/tasks/spells.vue 34 8 231 +./assets/scss/form.scss 49 2 227 +./components/static/groupPlans.vue 29 2 221 +./components/shops/shopItem.vue 32 0 220 +./components/header/notificationsDropdown.vue 24 28 208 +./components/group-plans/groupPlanOverviewModal.vue 27 1 207 +./components/chat/chatMessages.vue 19 9 203 +./components/static/communityGuidelines.vue 11 0 202 +./components/groups/questDetailsModal.vue 22 1 201 +./mixins/payments.js 37 10 189 +./components/inventory/equipment/equipGearModal.vue 30 0 188 +./mixins/guide.js 11 21 187 +./components/selectMembersModal.vue 21 1 186 +./store/actions/tasks.js 46 7 185 +./components/achievements/chooseClass.vue 18 0 183 +./components/avatar.vue 29 8 182 +./components/hall/heroes.vue 11 24 177 +./components/header/index.vue 13 3 173 +./components/groups/createPartyModal.vue 21 0 173 +./components/static/header.vue 24 5 168 +./components/groups/startQuestModal.vue 23 0 168 +./components/ui/drawerSlider.vue 20 1 168 +./components/ui/drawer.vue 20 3 166 +./components/groups/publicGuildItem.vue 19 2 165 +./components/groups/discovery.vue 23 12 163 +./store/actions/shops.js 30 6 157 +./components/header/notifications/worldBoss.vue 25 0 155 +./components/snackbars/notification.vue 16 2 155 +./components/groups/sidebar.vue 4 1 154 +./components/shops/market/keysToKennel.vue 9 7 152 +./components/group-plans/createGroupModalPages.vue 14 1 151 +./components/challenges/sidebar.vue 2 8 150 +./components/shops/market/sellModal.vue 31 0 147 +./components/static/staticWrapper.vue 33 0 145 +./components/challenges/findChallenges.vue 13 2 144 +./mixins/spells.js 25 11 143 +./store/actions/guilds.js 50 12 143 +./components/inventory/equipment/attributesGrid.vue 30 1 139 +./components/auth/authForm.vue 20 9 139 +./components/achievements/levelUp.vue 22 7 138 +./components/groups/myGuilds.vue 14 1 137 +./components/ui/toggleSwitch.vue 16 2 135 +./components/challenges/myChallenges.vue 14 2 135 +./components/header/notifications/base.vue 24 4 134 +./components/groups/inviteModal.vue 12 17 132 +./components/payments/sendGemsModal.vue 11 3 132 +./store/getters/tasks.js 19 15 127 +./components/tasks/approvalFooter.vue 11 3 126 +./components/chat/reportFlagModal.vue 13 0 120 +./components/static/pressKit.vue 6 1 119 +./components/world-boss/worldBossInfoModal.vue 18 1 118 +./assets/scss/button.scss 27 0 118 +./components/header/userDropdown.vue 12 0 115 +./components/settings/api.vue 9 4 113 +./components/tasks/tagsPopup.vue 15 0 112 +./components/shops/quests/questDialogDrops.vue 10 0 110 +./store/index.js 11 11 109 +./libs/analytics.js 24 14 103 +./store/actions/user.js 35 8 102 +./components/static/features.vue 5 0 100 +./components/achievements/lowHealth.vue 7 0 98 +./components/challenges/closeChallengeModal.vue 11 1 98 +./components/shops/quests/questInfo.vue 15 0 98 +./components/settings/restoreModal.vue 6 5 98 +./assets/scss/item.scss 22 5 97 +./components/ui/itemRows.vue 14 1 87 +./components/shops/balanceInfo.vue 15 0 87 +./assets/scss/dropdown.scss 16 0 84 +./components/settings/notifications.vue 9 5 84 +./components/tasks/brokenTaskModal.vue 7 2 84 +./components/world-boss/worldBossRageModal.vue 7 0 84 +./components/challenges/groupChallenges.vue 8 0 84 +./components/sidebarSection.vue 8 0 83 +./components/chat/autoComplete.vue 4 2 83 +./components/inventory/stable/mountRaisedModal.vue 14 0 83 +./assets/css/sprites.css 16 12 81 +./components/group-plans/billing.vue 2 0 78 +./components/inventory/stable/hatchedPetDialog.vue 10 0 78 +./components/inventory/stable/petItem.vue 5 0 78 +./store/actions/members.js 22 34 78 +./mixins/notifications.js 1 1 78 +./components/yesterdailyModal.vue 8 0 77 +./components/achievements/login-incentives.vue 7 0 77 +./components/ui/customMenuDropdown.vue 8 4 76 +./store/actions/chat.js 17 1 72 +./components/achievements/questInvitation.vue 2 0 72 +./store/actions/challenges.js 27 0 71 +./components/achievements/death.vue 3 2 70 +./mixins/groupsUtilities.js 18 4 70 +./components/inventory/item.vue 2 0 67 +./components/userMenu/achievements.vue 7 0 65 +./components/inventory/stable/foodItem.vue 4 0 65 +./components/static/faq.vue 6 6 65 +./components/achievements/achievementFooter.vue 10 5 64 +./components/achievements/questCompleted.vue 5 0 64 +./components/inventory/items/cards-modal.vue 3 0 64 +./components/chat/copyAsTodoModal.vue 4 1 63 +./components/achievements/ultimateGear.vue 4 1 62 +./assets/scss/typography.scss 12 1 62 +./components/header/notifications/groupTaskApproval.vue 8 3 61 +./components/settings/deleteModal.vue 2 0 60 +./components/header/notifications/questInvitation.vue 3 0 60 +./components/achievements/wonChallenge.vue 4 2 60 +./components/ui/drawerHeaderTabs.vue 7 0 58 +./libs/store/helpers/filterTasks.js 8 2 58 +./assets/scss/colors.scss 15 3 57 +./mixins/stats.js 12 0 57 +./components/header/notifications/guildInvitation.vue 8 0 56 +./store/actions/auth.js 15 1 56 +./components/achievements/newStuff.vue 4 1 56 +./components/settings/promoCode.vue 4 6 55 +./components/shops/quests/questDialogContent.vue 9 0 54 +./components/members/groupMemberSearchDropdown.vue 3 1 54 +./components/inventory/equipment/attributesPopover.vue 3 0 53 +./components/static/contact.vue 3 1 52 +./components/inventory/stable/mountItem.vue 2 0 51 +./libs/store/index.js 15 18 50 +./components/members/classBadge.vue 7 0 49 +./components/group-plans/index.vue 4 0 48 +./components/tasks/approvalModal.vue 2 0 48 +./components/achievements/streak.vue 4 1 48 +./assets/scss/categories.scss 7 0 47 +./components/404.vue 7 1 47 +./components/challenges/challengeMemberProgressModal.vue 3 0 46 +./components/snackbars/notifications.vue 5 1 45 +./libs/notifications.js 7 0 45 +./components/members/removeMemberModal.vue 3 0 45 +./components/members/memberSearchDropdown.vue 3 1 44 +./components/achievements/dropsEnabled.vue 3 0 44 +./components/ui/starBadge.vue 5 0 43 +./components/challenges/leaveChallengeModal.vue 3 0 42 +./components/header/notifications/newChatMessage.vue 3 1 42 +./components/header/notifications/unallocatedStatsPoints.vue 3 0 42 +./assets/css/sprites/spritesmith-largeSprites-0.css 0 0 42 +./components/achievements/rebirth.vue 4 1 41 +./components/groups/communityGuidelines.vue 6 0 41 +./components/achievements/contributor.vue 4 0 41 +./assets/scss/icon.scss 11 0 41 +./libs/asyncResource.js 4 3 41 +./components/achievements/welcome.vue 1 1 41 +./mixins/challengeUtilities.js 8 0 40 +./store/actions/tags.js 6 0 38 +./components/header/notifications/partyInvitation.vue 4 0 38 +./assets/scss/banner.scss 6 0 38 +./components/tasks/approvalHeader.vue 4 0 38 +./components/hall/patrons.vue 2 1 38 +./components/bannedAccountModal.vue 5 1 36 +./store/actions/index.js 4 2 36 +./libs/store/helpers/public.js 18 16 35 +./components/static/clearBrowserData.vue 2 1 34 +./components/achievements/invitedFriend.vue 3 1 34 +./components/achievements/joinedChallenge.vue 3 1 34 +./components/achievements/joinedGuild.vue 3 1 34 +./components/achievements/testingletiant.vue 2 0 34 +./components/header/notifications/cardReceived.vue 2 0 33 +./components/static/overview.vue 4 0 33 +./components/categories/categoryTags.vue 1 0 33 +./components/achievements/testing.vue 2 0 33 +./assets/scss/tiers.scss 9 0 32 +./assets/scss/modal.scss 7 0 32 +./components/header/notifications/newMysteryItems.vue 2 0 31 +./assets/scss/popover.scss 7 0 31 +./assets/scss/loading-screen.scss 4 0 31 +./libs/createAnimal.js 4 0 31 +./assets/scss/index.scss 4 3 29 +./components/achievements/rebirthEnabled.vue 3 0 29 +./components/header/messageCount.vue 4 0 29 +./components/ui/countBadge.vue 4 0 29 +./components/tasks/clearCompletedTodos.vue 4 0 29 +./components/settings/resetModal.vue 2 1 28 +./assets/scss/markdown.scss 7 0 28 +./libs/modform.js 9 1 28 +./components/header/notifications/newStuff.vue 2 0 27 +./main.js 7 7 27 +./assets/scss/badge.scss 4 0 26 +./components/secondaryMenu.vue 5 0 26 +./components/achievements/armoireEmpty.vue 2 0 26 +./components/header/notifications/newInboxMessage.vue 2 0 26 +./store/actions/notifications.js 4 0 25 +./components/groups/groupGemsModal.vue 2 0 25 +./index.html 2 2 25 +./components/header/notifications/groupTaskNeedsWork.vue 2 0 25 +./components/shops/index.vue 2 0 25 +./components/static/merch.vue 2 6 25 +./components/header/notifications/groupTaskApproved.vue 2 0 25 +./assets/scss/static.scss 7 0 25 +./components/shops/_currencyMixin.js 1 0 24 +./mixins/openedItemRows.js 4 1 24 +./libs/store/helpers/orderTasks.js 4 3 24 +./components/achievements/achievementAvatar.vue 2 1 23 +./components/groupLink.vue 2 0 23 +./store/actions/common.js 3 6 23 +./store/actions/hall.js 5 0 23 +./store/actions/party.js 2 0 23 +./assets/scss/pin.scss 4 0 22 +./libs/i18n.js 6 6 22 +./filters/roundBigNumber.js 4 0 21 +./libs/payments.js 6 5 21 +./libs/userlocalManager.js 3 1 21 +./assets/scss/page.scss 4 0 20 +./components/userLink.vue 1 0 20 +./components/static/newStuff.vue 3 0 20 +./components/settings/index.vue 3 0 20 +./components/groups/index.vue 4 0 20 +./directives/mouseposition.directive.js 8 4 19 +./mixins/challengeMemberSearch.js 1 1 19 +./components/groups/newPartyModal.pug 1 0 19 +./directives/resize.directive.js 8 4 19 +./mixins/buy.js 3 0 19 +./assets/scss/animals.scss 3 0 17 +./store/actions/quests.js 7 5 17 +./components/inventory/index.vue 2 0 17 +./libs/auth.js 6 0 17 +./components/userMenu/profilePage.vue 2 2 17 +./mixins/styleHelper.js 0 0 17 +./components/hall/index.vue 3 0 16 +./components/challenges/index.vue 3 0 16 +./assets/scss/misc.scss 1 0 16 +./store/getters/user.js 4 0 15 +./assets/scss/tooltip.scss 2 0 15 +./store/getters/index.js 3 2 14 +./assets/scss/progress-bar.scss 3 1 13 +./components/settings/dataExport.vue 0 0 13 +./README.md 9 0 12 +./directives/dragdrop.directive.js 14 72 12 +./libs/deepFreeze.js 3 6 11 +./store/getters/members.js 1 0 11 +./store/actions/snackbars.js 2 0 11 +./assets/scss/utils.scss 3 7 10 +./filters/registerGlobals.js 1 0 9 +./libs/logging.js 0 0 9 +./libs/store/helpers/internals.js 5 18 9 +./assets/scss/stats.scss 2 0 9 +./assets/scss/dragdrop.scss 1 0 8 +./components/static/app.vue 1 3 7 +./store/actions/world-state.js 1 0 6 +./directives/markdown.js 2 0 6 +./components/page.vue 0 0 6 +./store/getters/shops.js 1 0 6 +./directives/directive.common.js 1 1 6 +./assets/scss/variables.scss 1 3 5 +./components/parentPage.vue 0 0 5 +./libs/encodeParams.js 1 1 5 +./assets/scss/bootstrap.scss 2 3 3 +./components/emptyView.vue 0 1 3 +./components/header/notifications/challengeInvitation.vue 0 0 3 +./filters/round.js 0 0 3 +./store/getters/party.js 0 0 3 +./filters/floorWholeNumber.js 0 0 3 +./filters/floor.js 0 0 3 +./assets/svg/README.md 0 0 1 +--------------------------------------------------------------------------------------------------------------- +SUM: 4571 1246 83885 +--------------------------------------------------------------------------------------------------------------- From dc5722d0debe421630c9fa1f69b15fa9826f8f11 Mon Sep 17 00:00:00 2001 From: negue Date: Fri, 8 Jun 2018 20:26:26 +0200 Subject: [PATCH 06/45] report & mark message --- website/client/components/chat/chatCard.vue | 29 +++++++++-- .../components/chat/reportFlagModal.vue | 12 ++++- website/client/store/actions/chat.js | 3 ++ website/common/locales/en/messages.json | 4 +- website/server/controllers/api-v3/members.js | 16 +++++-- .../libs/chatReporting/inboxChatReporter.js | 48 ++++++++++++------- 6 files changed, 86 insertions(+), 26 deletions(-) diff --git a/website/client/components/chat/chatCard.vue b/website/client/components/chat/chatCard.vue index e2db8411b1..687fa7457b 100644 --- a/website/client/components/chat/chatCard.vue +++ b/website/client/components/chat/chatCard.vue @@ -13,6 +13,8 @@ div .svg-icon(v-html="tierIcon", v-if='showShowTierStyle') p.time {{msg.timestamp | timeAgo}} .text(v-markdown='msg.text') + .reported(v-if="isMessageReported") + span(v-once) {{ $t('reportedMessage')}} hr .action(@click='like()', v-if='!inbox && msg.likes', :class='{active: msg.likes[user._id]}') .svg-icon(v-html="icons.like") @@ -20,13 +22,13 @@ div span(v-if='msg.likes[user._id]') {{ $t('liked') }} span.action(v-if='!inbox', @click='copyAsTodo(msg)') .svg-icon(v-html="icons.copy", v-once) - | {{$t('copyAsTodo')}} - span.action(v-if='inbox || (user.flags.communityGuidelinesAccepted && msg.uuid !== "system")', @click='report(msg)') + span(v-once) {{$t('copyAsTodo')}} + span.action(v-if='inbox || (user.flags.communityGuidelinesAccepted && msg.uuid !== "system" && !isMessageReported)', @click='report(msg)') .svg-icon(v-html="icons.report", v-once) - | {{$t('report')}} + span(v-once) {{$t('report')}} span.action(v-if='msg.uuid === user._id || inbox || user.contributor.admin', @click='remove()') .svg-icon(v-html="icons.delete", v-once) - | {{$t('delete')}} + span(v-once) {{$t('delete')}} span.action.float-right.liked(v-if='likeCount > 0') .svg-icon(v-html="icons.liked") | + {{ likeCount }} @@ -34,6 +36,7 @@ div diff --git a/website/client/components/tasks/task.vue b/website/client/components/tasks/task.vue index 16a03ed681..4d7f383f47 100644 --- a/website/client/components/tasks/task.vue +++ b/website/client/components/tasks/task.vue @@ -20,7 +20,7 @@ v-if="isUser && !isRunningYesterdailies", :right="task.type === 'reward'", ref="taskDropdown", - v-b-tooltip.hover.top="$t('showMore')" + v-b-tooltip.hover.top="$t('options')" ) div(slot="dropdown-toggle", draggable=false) .svg-icon.dropdown-icon(v-html="icons.menu") @@ -141,6 +141,11 @@ min-width: 0px; overflow-wrap: break-word; + // markdown p-tag, can't find without /deep/ + /deep/ p { + margin-bottom: 0; + } + &.has-notes { padding-bottom: 4px; } @@ -229,7 +234,7 @@ overflow-wrap: break-word; &.has-checklist { - padding-bottom: 8px; + padding-bottom: 2px; } } @@ -285,7 +290,7 @@ margin-bottom: -3px; min-height: 0px; width: 100%; - margin-left: 8px; + margin-left: 0; padding-right: 20px; overflow-wrap: break-word; @@ -427,7 +432,7 @@ border-left: none; } - .task-control, .reward-control { + .task-control:not(.task-disabled-habit-control-inner), .reward-control { cursor: pointer; } diff --git a/website/client/components/tasks/user.vue b/website/client/components/tasks/user.vue index e9652ccabc..4986ecec29 100644 --- a/website/client/components/tasks/user.vue +++ b/website/client/components/tasks/user.vue @@ -12,7 +12,7 @@ .col-12.col-md-4.offset-md-4 .d-flex input.form-control.input-search(type="text", :placeholder="$t('search')", v-model="searchText") - button.btn.btn-secondary.dropdown-toggle.ml-2.d-flex.align-items-center( + button.btn.btn-secondary.dropdown-toggle.ml-2.d-flex.align-items-center.search-button( type="button", @click="toggleFilterPanel()", :class="{active: selectedTags.length > 0}", @@ -110,6 +110,10 @@ padding-top: 16px; } + .input-search, .search-button { + height: 40px; + } + .tasks-navigation { margin-bottom: 20px; } diff --git a/website/common/locales/en/generic.json b/website/common/locales/en/generic.json index 57adad943b..f8fa686631 100644 --- a/website/common/locales/en/generic.json +++ b/website/common/locales/en/generic.json @@ -34,6 +34,7 @@ "saveEdits": "Save Edits", "showMore": "Show More", "showLess": "Show Less", + "options": "Options", "expandToolbar": "Expand Toolbar", "collapseToolbar": "Collapse Toolbar", From 1d8a5b19527bb52d5be1ca8c7cd9c90f84ccb92d Mon Sep 17 00:00:00 2001 From: Alec Brickner Date: Sun, 3 Mar 2019 08:39:42 -0800 Subject: [PATCH 39/45] Keep mystery items synced with server (#11039) --- website/client/components/inventory/items/index.vue | 2 -- website/client/store/actions/user.js | 5 ++++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/website/client/components/inventory/items/index.vue b/website/client/components/inventory/items/index.vue index 73354f32ef..6ffae15b42 100644 --- a/website/client/components/inventory/items/index.vue +++ b/website/client/components/inventory/items/index.vue @@ -469,8 +469,6 @@ export default { let openedItem = result.data.data; let text = this.content.gear.flat[openedItem.key].text(); this.drop(this.$t('messageDropMysteryItem', {dropText: text}), openedItem); - item.quantity--; - this.$forceUpdate(); } else { this.$root.$emit('selectMembersModal::showItem', item); } diff --git a/website/client/store/actions/user.js b/website/client/store/actions/user.js index d15bd3e805..fe6094fcc9 100644 --- a/website/client/store/actions/user.js +++ b/website/client/store/actions/user.js @@ -5,6 +5,7 @@ import axios from 'axios'; import { togglePinnedItem as togglePinnedItemOp } from 'common/script/ops/pinnedGearUtils'; import changeClassOp from 'common/script/ops/changeClass'; import disableClassesOp from 'common/script/ops/disableClasses'; +import openMysteryItemOp from 'common/script/ops/openMysteryItem'; export function fetch (store, options = {}) { // eslint-disable-line no-shadow return loadAsyncResource({ @@ -127,7 +128,9 @@ export function castSpell (store, params) { return axios.post(spellUrl, data); } -export function openMysteryItem () { +export async function openMysteryItem (store) { + let user = store.state.user.data; + openMysteryItemOp(user); return axios.post('/api/v4/user/open-mystery-item'); } From 17d8a7b7066cc0525996c8a736839f5528abaaff Mon Sep 17 00:00:00 2001 From: Alec Brickner Date: Sun, 3 Mar 2019 08:48:51 -0800 Subject: [PATCH 40/45] Fix mismatch between pet and star badge (#11041) --- website/client/components/inventory/stable/index.vue | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/website/client/components/inventory/stable/index.vue b/website/client/components/inventory/stable/index.vue index ba996e539e..8e28554320 100644 --- a/website/client/components/inventory/stable/index.vue +++ b/website/client/components/inventory/stable/index.vue @@ -92,7 +92,11 @@ @click="petClicked(item)" ) template(slot="itemBadge", slot-scope="context") - starBadge(:selected="item.key === currentPet", :show="isOwned('pet', item)", @click="selectPet(item)") + starBadge( + :selected="context.item.key === currentPet", + :show="isOwned('pet', context.item)", + @click="selectPet(context.item)" + ) .btn.btn-flat.btn-show-more(@click="setShowMore(petGroup.key)", v-if='petGroup.key !== "specialPets"') | {{ $_openedItemRows_isToggled(petGroup.key) ? $t('showLess') : $t('showMore') }} From 77f3bb53dea34625a965eb2d6bd418ce76878c7b Mon Sep 17 00:00:00 2001 From: Matteo Pagliazzi Date: Mon, 4 Mar 2019 21:51:17 +0100 Subject: [PATCH 41/45] New payments buttons and new Amazon checkout flow (#10940) * start implementing separate amazon button component * wio * switch amazon to new flow --- .../group-plans/createGroupModalPages.vue | 13 +- .../client/components/groups/groupPlan.vue | 16 ++- .../components/payments/amazonButton.vue | 110 +++++++++++++++++ .../components/payments/amazonModal.vue | 114 ++++-------------- .../components/payments/buyGemsModal.vue | 14 +-- .../components/payments/sendGemsModal.vue | 7 +- .../components/settings/subscription.vue | 15 ++- website/client/mixins/payments.js | 24 +++- 8 files changed, 195 insertions(+), 118 deletions(-) create mode 100644 website/client/components/payments/amazonButton.vue diff --git a/website/client/components/group-plans/createGroupModalPages.vue b/website/client/components/group-plans/createGroupModalPages.vue index e0971792a8..38630835ad 100644 --- a/website/client/components/group-plans/createGroupModalPages.vue +++ b/website/client/components/group-plans/createGroupModalPages.vue @@ -25,7 +25,8 @@ .payment-providers .box.payment-button(@click='pay(PAYMENTS.STRIPE)') .svg-icon.credit-card-icon(v-html="icons.creditCard") - .box.payment-button.amazon(@click='pay(PAYMENTS.AMAZON)') + amazon-button(:amazon-data="pay(PAYMENTS.AMAZON)") + //.box.payment-button.amazon(@click='pay(PAYMENTS.AMAZON)') .svg-icon.amazon-pay-icon(v-html="icons.amazonpay") @@ -87,17 +88,21 @@ import * as Analytics from 'client/libs/analytics'; import { mapState } from 'client/libs/store'; import paymentsMixin from '../../mixins/payments'; +import amazonButton from 'client/components/payments/amazonButton'; -import amazonpay from 'assets/svg/amazonpay.svg'; +// import amazonpay from 'assets/svg/amazonpay.svg'; import creditCard from 'assets/svg/credit-card.svg'; export default { mixins: [paymentsMixin], + components: { + amazonButton, + }, data () { return { amazonPayments: {}, icons: Object.freeze({ - amazonpay, + // amazonpay, creditCard, }), PAGES: { @@ -159,7 +164,7 @@ export default { this.showStripe(paymentData); } else if (this.paymentMethod === this.PAYMENTS.AMAZON) { paymentData.type = 'subscription'; - this.amazonPaymentsInit(paymentData); + return paymentData; } }, }, diff --git a/website/client/components/groups/groupPlan.vue b/website/client/components/groups/groupPlan.vue index 9e92d0aa58..968b442703 100644 --- a/website/client/components/groups/groupPlan.vue +++ b/website/client/components/groups/groupPlan.vue @@ -53,7 +53,8 @@ div .svg-icon.credit-card-icon(v-html="icons.group") p.credit-card Credit Card p Powered by Stripe - .box.payment-button(@click='pay(PAYMENTS.AMAZON)') + amazon-button(:amazon-data="pay(PAYMENTS.AMAZON)") + //.box.payment-button(@click='pay(PAYMENTS.AMAZON)') .svg-icon.amazon-pay-icon(v-html="icons.amazonpay") .container.col-6.offset-3.create-option(v-if='!upgradingGroup._id') @@ -104,7 +105,8 @@ div .box.payment-button(@click='pay(PAYMENTS.STRIPE)') p Credit Card p Powered by Stripe - .box.payment-button(@click='pay(PAYMENTS.AMAZON)') + amazon-button(:amazon-data="pay(PAYMENTS.AMAZON)") + //.box.payment-button(@click='pay(PAYMENTS.AMAZON)') | Amazon Pay @@ -334,17 +336,21 @@ div import paymentsMixin from '../../mixins/payments'; import { mapState } from 'client/libs/store'; import group from 'assets/svg/group.svg'; -import amazonpay from 'assets/svg/amazonpay.svg'; +// import amazonpay from 'assets/svg/amazonpay.svg'; import positiveIcon from 'assets/svg/positive.svg'; +import amazonButton from 'client/components/payments/amazonButton'; export default { mixins: [paymentsMixin], + components: { + amazonButton, + }, data () { return { amazonPayments: {}, icons: Object.freeze({ group, - amazonpay, + // amazonpay, positiveIcon, }), PAGES: { @@ -413,7 +419,7 @@ export default { this.showStripe(paymentData); } else if (this.paymentMethod === this.PAYMENTS.AMAZON) { paymentData.type = 'subscription'; - this.amazonPaymentsInit(paymentData); + return paymentData; } }, }, diff --git a/website/client/components/payments/amazonButton.vue b/website/client/components/payments/amazonButton.vue new file mode 100644 index 0000000000..35e33c2af7 --- /dev/null +++ b/website/client/components/payments/amazonButton.vue @@ -0,0 +1,110 @@ + + + + + diff --git a/website/client/components/payments/amazonModal.vue b/website/client/components/payments/amazonModal.vue index 13380db0b5..823aae5e85 100644 --- a/website/client/components/payments/amazonModal.vue +++ b/website/client/components/payments/amazonModal.vue @@ -1,7 +1,6 @@ @@ -86,12 +87,16 @@ import { mapState } from 'client/libs/store'; import planGemLimits from '../../../common/script/libs/planGemLimits'; import paymentsMixin from 'client/mixins/payments'; import notificationsMixin from 'client/mixins/notifications'; +import amazonButton from 'client/components/payments/amazonButton'; // @TODO: EMAILS.TECH_ASSISTANCE_EMAIL, load from config const TECH_ASSISTANCE_EMAIL = 'admin@habitica.com'; export default { mixins: [paymentsMixin, notificationsMixin], + components: { + amazonButton, + }, data () { return { planGemLimits, diff --git a/website/client/components/settings/subscription.vue b/website/client/components/settings/subscription.vue index b76747c523..14c7223c6c 100644 --- a/website/client/components/settings/subscription.vue +++ b/website/client/components/settings/subscription.vue @@ -80,7 +80,8 @@ a.purchase(@click="openPaypal(paypalPurchaseLink, 'subscription')", :disabled='!subscription.key') img(src='https://www.paypalobjects.com/webstatic/en_US/i/buttons/pp-acceptance-small.png', :alt="$t('paypal')") .col-md-4 - a.btn.btn-secondary.purchase(@click="payWithAmazon()") + amazon-button(:amazon-data="{type: 'subscription', subscription: this.subscription.key, coupon: this.subscription.coupon}") + // a.btn.btn-secondary.purchase(@click="payWithAmazon()") img(src='https://payments.amazon.com/gp/cba/button', :alt="$t('amazonPayments')") .row .col-6 @@ -115,8 +116,13 @@ import planGemLimits from '../../../common/script/libs/planGemLimits'; import paymentsMixin from '../../mixins/payments'; import notificationsMixin from '../../mixins/notifications'; +import amazonButton from 'client/components/payments/amazonButton'; + export default { mixins: [paymentsMixin, notificationsMixin], + components: { + amazonButton, + }, data () { return { loading: false, @@ -240,13 +246,6 @@ export default { }, }, methods: { - payWithAmazon () { - this.amazonPaymentsInit({ - type: 'subscription', - subscription: this.subscription.key, - coupon: this.subscription.coupon, - }); - }, async applyCoupon (coupon) { const response = await axios.post(`/api/v4/coupons/validate/${coupon}`); diff --git a/website/client/mixins/payments.js b/website/client/mixins/payments.js index 84c653a45f..9fc28dda27 100644 --- a/website/client/mixins/payments.js +++ b/website/client/mixins/payments.js @@ -251,8 +251,30 @@ export default { this.amazonPayments.gift = data.gift; this.amazonPayments.type = data.type; + }, + amazonOnError (error) { + alert(error.getErrorMessage()); + this.reset(); + }, + reset () { + // @TODO: Ensure we are using all of these + // some vars are set in the payments mixin. We should try to edit in one place + this.amazonPayments.modal = null; + this.amazonPayments.type = null; + this.amazonPayments.loggedIn = false; - this.$root.$emit('habitica::pay-with-amazon', this.amazonPayments); + // Gift + this.amazonPayments.gift = null; + this.amazonPayments.giftReceiver = null; + + this.amazonPayments.billingAgreementId = null; + this.amazonPayments.orderReferenceId = null; + this.amazonPayments.paymentSelected = false; + this.amazonPayments.recurringConsent = false; + this.amazonPayments.subscription = null; + this.amazonPayments.coupon = null; + this.amazonPayments.groupToCreate = null; + this.amazonPayments.group = null; }, async cancelSubscription (config) { if (config && config.group && !confirm(this.$t('confirmCancelGroupPlan'))) return; From 298a79b58d334c874d2d08726d60aac1eb261588 Mon Sep 17 00:00:00 2001 From: Matteo Pagliazzi Date: Mon, 4 Mar 2019 22:00:40 +0100 Subject: [PATCH 42/45] fix(amazon): make sure to use up to date data --- website/client/components/payments/amazonButton.vue | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/website/client/components/payments/amazonButton.vue b/website/client/components/payments/amazonButton.vue index 35e33c2af7..bd6e12b5cd 100644 --- a/website/client/components/payments/amazonButton.vue +++ b/website/client/components/payments/amazonButton.vue @@ -60,7 +60,7 @@ export default { this.buttonId = `AmazonPayButton-${uuid.v4()}`; }, mounted () { - this.amazonPaymentsInit(this.amazonData); // TOOD clone + this.amazonPaymentsInit(this.amazonData); if (this.isAmazonReady) return this.setupAmazon(); this.$store.watch(state => state.isAmazonReady, (isAmazonReady) => { @@ -90,6 +90,8 @@ export default { this.$root.$emit('habitica::pay-with-amazon', this.amazonPayments); }, authorization: () => { + this.amazonPaymentsInit(this.amazonData); + window.amazon.Login.authorize({ scope: 'payments:widget', popup: true, From 660928323eb89f81d70deb8e18a4b6af049cabae Mon Sep 17 00:00:00 2001 From: negue Date: Mon, 4 Mar 2019 22:01:45 +0100 Subject: [PATCH 43/45] fix header design (#11013) * statsbar-component for a closer tooltip * fix scss scope, twice --- website/client/components/memberDetails.vue | 102 ++++---------- website/client/components/ui/statsbar.vue | 126 ++++++++++++++++++ .../client/components/userMenu/profile.vue | 3 + 3 files changed, 156 insertions(+), 75 deletions(-) create mode 100644 website/client/components/ui/statsbar.vue diff --git a/website/client/components/memberDetails.vue b/website/client/components/memberDetails.vue index bd5bcd27e1..9a016666a4 100644 --- a/website/client/components/memberDetails.vue +++ b/website/client/components/memberDetails.vue @@ -23,21 +23,31 @@ span.mr-1(v-if="member.auth && member.auth.local && member.auth.local.username") @{{ member.auth.local.username }} span.mr-1(v-if="member.auth && member.auth.local && member.auth.local.username") • span {{ characterLevel }} - .progress-container(v-b-tooltip.hover.bottom="$t('health')") - .svg-icon(v-html="icons.health") - .progress - .progress-bar.bg-health(:style="{width: `${percent(member.stats.hp, MAX_HEALTH)}%`}") - span.small-text {{member.stats.hp | statFloor}} / {{MAX_HEALTH}} - .progress-container(v-b-tooltip.hover.bottom="$t('experience')") - .svg-icon(v-html="icons.experience") - .progress - .progress-bar.bg-experience(:style="{width: `${percent(member.stats.exp, toNextLevel)}%`}") - span.small-text {{member.stats.exp | statFloor}} / {{toNextLevel}} - .progress-container(v-if="hasClass", v-b-tooltip.hover.bottom="$t('mana')") - .svg-icon(v-html="icons.mana") - .progress - .progress-bar.bg-mana(:style="{width: `${percent(member.stats.mp, maxMP)}%`}") - span.small-text {{member.stats.mp | statFloor}} / {{maxMP}} + stats-bar( + :icon="icons.health", + :value="member.stats.hp", + :maxValue="MAX_HEALTH", + :tooltip="$t('health')", + progressClass="bg-health", + :condensed="condensed" + ) + stats-bar( + :icon="icons.experience", + :value="member.stats.exp", + :maxValue="toNextLevel", + :tooltip="$t('experience')", + progressClass="bg-experience", + :condensed="condensed" + ) + stats-bar( + v-if="hasClass", + :icon="icons.mana", + :value="member.stats.mp", + :maxValue="maxMP", + :tooltip="$t('mana')", + progressClass="bg-mana", + :condensed="condensed" + ) @@ -190,6 +143,7 @@ import Avatar from './avatar'; import ClassBadge from './members/classBadge'; import { mapState } from 'client/libs/store'; import Profile from './userMenu/profile'; +import StatsBar from './ui/statsbar'; import { toNextLevel } from '../../common/script/statHelpers'; import statsComputed from '../../common/script/libs/statsComputed'; @@ -205,9 +159,7 @@ export default { Avatar, Profile, ClassBadge, - }, - directives: { - // bTooltip, + StatsBar, }, props: { member: { diff --git a/website/client/components/ui/statsbar.vue b/website/client/components/ui/statsbar.vue new file mode 100644 index 0000000000..dc7ddb37c0 --- /dev/null +++ b/website/client/components/ui/statsbar.vue @@ -0,0 +1,126 @@ + + + + + diff --git a/website/client/components/userMenu/profile.vue b/website/client/components/userMenu/profile.vue index 9f3de16db0..e66208d0d8 100644 --- a/website/client/components/userMenu/profile.vue +++ b/website/client/components/userMenu/profile.vue @@ -324,6 +324,7 @@ } .progress-container > .progress { + border-radius: 1px; background-color: $gray-500; } } @@ -371,8 +372,10 @@ .progress { height: 8px; + border-radius: 1px; .progress-bar { + border-radius: 1px; background-color: $green-10 !important; } } From bc91dd81e90b8f086bb60be1b31f3bbcd7a1f2b4 Mon Sep 17 00:00:00 2001 From: Matteo Pagliazzi Date: Thu, 7 Mar 2019 16:58:38 +0100 Subject: [PATCH 44/45] Revert "fix(amazon): make sure to use up to date data" This reverts commit 298a79b58d334c874d2d08726d60aac1eb261588. --- website/client/components/payments/amazonButton.vue | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/website/client/components/payments/amazonButton.vue b/website/client/components/payments/amazonButton.vue index bd6e12b5cd..35e33c2af7 100644 --- a/website/client/components/payments/amazonButton.vue +++ b/website/client/components/payments/amazonButton.vue @@ -60,7 +60,7 @@ export default { this.buttonId = `AmazonPayButton-${uuid.v4()}`; }, mounted () { - this.amazonPaymentsInit(this.amazonData); + this.amazonPaymentsInit(this.amazonData); // TOOD clone if (this.isAmazonReady) return this.setupAmazon(); this.$store.watch(state => state.isAmazonReady, (isAmazonReady) => { @@ -90,8 +90,6 @@ export default { this.$root.$emit('habitica::pay-with-amazon', this.amazonPayments); }, authorization: () => { - this.amazonPaymentsInit(this.amazonData); - window.amazon.Login.authorize({ scope: 'payments:widget', popup: true, From 2a367ab3a7c7227741833b10c65ae398b9efeea2 Mon Sep 17 00:00:00 2001 From: Matteo Pagliazzi Date: Thu, 7 Mar 2019 16:58:49 +0100 Subject: [PATCH 45/45] Revert "New payments buttons and new Amazon checkout flow (#10940)" This reverts commit 77f3bb53dea34625a965eb2d6bd418ce76878c7b. --- .../group-plans/createGroupModalPages.vue | 13 +- .../client/components/groups/groupPlan.vue | 16 +-- .../components/payments/amazonButton.vue | 110 ----------------- .../components/payments/amazonModal.vue | 114 ++++++++++++++---- .../components/payments/buyGemsModal.vue | 14 +-- .../components/payments/sendGemsModal.vue | 7 +- .../components/settings/subscription.vue | 15 +-- website/client/mixins/payments.js | 24 +--- 8 files changed, 118 insertions(+), 195 deletions(-) delete mode 100644 website/client/components/payments/amazonButton.vue diff --git a/website/client/components/group-plans/createGroupModalPages.vue b/website/client/components/group-plans/createGroupModalPages.vue index 38630835ad..e0971792a8 100644 --- a/website/client/components/group-plans/createGroupModalPages.vue +++ b/website/client/components/group-plans/createGroupModalPages.vue @@ -25,8 +25,7 @@ .payment-providers .box.payment-button(@click='pay(PAYMENTS.STRIPE)') .svg-icon.credit-card-icon(v-html="icons.creditCard") - amazon-button(:amazon-data="pay(PAYMENTS.AMAZON)") - //.box.payment-button.amazon(@click='pay(PAYMENTS.AMAZON)') + .box.payment-button.amazon(@click='pay(PAYMENTS.AMAZON)') .svg-icon.amazon-pay-icon(v-html="icons.amazonpay") @@ -88,21 +87,17 @@ import * as Analytics from 'client/libs/analytics'; import { mapState } from 'client/libs/store'; import paymentsMixin from '../../mixins/payments'; -import amazonButton from 'client/components/payments/amazonButton'; -// import amazonpay from 'assets/svg/amazonpay.svg'; +import amazonpay from 'assets/svg/amazonpay.svg'; import creditCard from 'assets/svg/credit-card.svg'; export default { mixins: [paymentsMixin], - components: { - amazonButton, - }, data () { return { amazonPayments: {}, icons: Object.freeze({ - // amazonpay, + amazonpay, creditCard, }), PAGES: { @@ -164,7 +159,7 @@ export default { this.showStripe(paymentData); } else if (this.paymentMethod === this.PAYMENTS.AMAZON) { paymentData.type = 'subscription'; - return paymentData; + this.amazonPaymentsInit(paymentData); } }, }, diff --git a/website/client/components/groups/groupPlan.vue b/website/client/components/groups/groupPlan.vue index 968b442703..9e92d0aa58 100644 --- a/website/client/components/groups/groupPlan.vue +++ b/website/client/components/groups/groupPlan.vue @@ -53,8 +53,7 @@ div .svg-icon.credit-card-icon(v-html="icons.group") p.credit-card Credit Card p Powered by Stripe - amazon-button(:amazon-data="pay(PAYMENTS.AMAZON)") - //.box.payment-button(@click='pay(PAYMENTS.AMAZON)') + .box.payment-button(@click='pay(PAYMENTS.AMAZON)') .svg-icon.amazon-pay-icon(v-html="icons.amazonpay") .container.col-6.offset-3.create-option(v-if='!upgradingGroup._id') @@ -105,8 +104,7 @@ div .box.payment-button(@click='pay(PAYMENTS.STRIPE)') p Credit Card p Powered by Stripe - amazon-button(:amazon-data="pay(PAYMENTS.AMAZON)") - //.box.payment-button(@click='pay(PAYMENTS.AMAZON)') + .box.payment-button(@click='pay(PAYMENTS.AMAZON)') | Amazon Pay @@ -336,21 +334,17 @@ div import paymentsMixin from '../../mixins/payments'; import { mapState } from 'client/libs/store'; import group from 'assets/svg/group.svg'; -// import amazonpay from 'assets/svg/amazonpay.svg'; +import amazonpay from 'assets/svg/amazonpay.svg'; import positiveIcon from 'assets/svg/positive.svg'; -import amazonButton from 'client/components/payments/amazonButton'; export default { mixins: [paymentsMixin], - components: { - amazonButton, - }, data () { return { amazonPayments: {}, icons: Object.freeze({ group, - // amazonpay, + amazonpay, positiveIcon, }), PAGES: { @@ -419,7 +413,7 @@ export default { this.showStripe(paymentData); } else if (this.paymentMethod === this.PAYMENTS.AMAZON) { paymentData.type = 'subscription'; - return paymentData; + this.amazonPaymentsInit(paymentData); } }, }, diff --git a/website/client/components/payments/amazonButton.vue b/website/client/components/payments/amazonButton.vue deleted file mode 100644 index 35e33c2af7..0000000000 --- a/website/client/components/payments/amazonButton.vue +++ /dev/null @@ -1,110 +0,0 @@ - - - - - diff --git a/website/client/components/payments/amazonModal.vue b/website/client/components/payments/amazonModal.vue index 823aae5e85..13380db0b5 100644 --- a/website/client/components/payments/amazonModal.vue +++ b/website/client/components/payments/amazonModal.vue @@ -1,6 +1,7 @@ @@ -87,16 +86,12 @@ import { mapState } from 'client/libs/store'; import planGemLimits from '../../../common/script/libs/planGemLimits'; import paymentsMixin from 'client/mixins/payments'; import notificationsMixin from 'client/mixins/notifications'; -import amazonButton from 'client/components/payments/amazonButton'; // @TODO: EMAILS.TECH_ASSISTANCE_EMAIL, load from config const TECH_ASSISTANCE_EMAIL = 'admin@habitica.com'; export default { mixins: [paymentsMixin, notificationsMixin], - components: { - amazonButton, - }, data () { return { planGemLimits, diff --git a/website/client/components/settings/subscription.vue b/website/client/components/settings/subscription.vue index 14c7223c6c..b76747c523 100644 --- a/website/client/components/settings/subscription.vue +++ b/website/client/components/settings/subscription.vue @@ -80,8 +80,7 @@ a.purchase(@click="openPaypal(paypalPurchaseLink, 'subscription')", :disabled='!subscription.key') img(src='https://www.paypalobjects.com/webstatic/en_US/i/buttons/pp-acceptance-small.png', :alt="$t('paypal')") .col-md-4 - amazon-button(:amazon-data="{type: 'subscription', subscription: this.subscription.key, coupon: this.subscription.coupon}") - // a.btn.btn-secondary.purchase(@click="payWithAmazon()") + a.btn.btn-secondary.purchase(@click="payWithAmazon()") img(src='https://payments.amazon.com/gp/cba/button', :alt="$t('amazonPayments')") .row .col-6 @@ -116,13 +115,8 @@ import planGemLimits from '../../../common/script/libs/planGemLimits'; import paymentsMixin from '../../mixins/payments'; import notificationsMixin from '../../mixins/notifications'; -import amazonButton from 'client/components/payments/amazonButton'; - export default { mixins: [paymentsMixin, notificationsMixin], - components: { - amazonButton, - }, data () { return { loading: false, @@ -246,6 +240,13 @@ export default { }, }, methods: { + payWithAmazon () { + this.amazonPaymentsInit({ + type: 'subscription', + subscription: this.subscription.key, + coupon: this.subscription.coupon, + }); + }, async applyCoupon (coupon) { const response = await axios.post(`/api/v4/coupons/validate/${coupon}`); diff --git a/website/client/mixins/payments.js b/website/client/mixins/payments.js index 9fc28dda27..84c653a45f 100644 --- a/website/client/mixins/payments.js +++ b/website/client/mixins/payments.js @@ -251,30 +251,8 @@ export default { this.amazonPayments.gift = data.gift; this.amazonPayments.type = data.type; - }, - amazonOnError (error) { - alert(error.getErrorMessage()); - this.reset(); - }, - reset () { - // @TODO: Ensure we are using all of these - // some vars are set in the payments mixin. We should try to edit in one place - this.amazonPayments.modal = null; - this.amazonPayments.type = null; - this.amazonPayments.loggedIn = false; - // Gift - this.amazonPayments.gift = null; - this.amazonPayments.giftReceiver = null; - - this.amazonPayments.billingAgreementId = null; - this.amazonPayments.orderReferenceId = null; - this.amazonPayments.paymentSelected = false; - this.amazonPayments.recurringConsent = false; - this.amazonPayments.subscription = null; - this.amazonPayments.coupon = null; - this.amazonPayments.groupToCreate = null; - this.amazonPayments.group = null; + this.$root.$emit('habitica::pay-with-amazon', this.amazonPayments); }, async cancelSubscription (config) { if (config && config.group && !confirm(this.$t('confirmCancelGroupPlan'))) return;