From 226112e73b5fef7b39f82dd00dd1fe66181c0f84 Mon Sep 17 00:00:00 2001 From: Sebin Song Date: Sat, 19 Aug 2023 02:13:35 +0900 Subject: [PATCH] #1686 - Fix a few bugs in payment-detail modal (#1688) * fix several bugs in payment-detail modal * fix the issue where PaymentDetails Modal is not responsive * make addition fix * hide 'Cancel payment' button for an old transaction item --- frontend/model/state.js | 2 +- .../containers/payments/PaymentDetail.vue | 29 +++++++++++++++---- .../containers/payments/PaymentRowSent.vue | 10 +++++-- .../containers/payments/PaymentsMixin.js | 3 +- frontend/views/pages/Payments.vue | 18 ++++++++---- 5 files changed, 48 insertions(+), 14 deletions(-) diff --git a/frontend/model/state.js b/frontend/model/state.js index bfa46d67ca..1cde707acb 100644 --- a/frontend/model/state.js +++ b/frontend/model/state.js @@ -366,7 +366,7 @@ const getters = { for (const toUser in paymentsFrom[ourUsername]) { for (const paymentHash of paymentsFrom[ourUsername][toUser]) { const { data, meta } = allPayments[paymentHash] - payments.push({ hash: paymentHash, data, meta, amount: data.amount }) + payments.push({ hash: paymentHash, data, meta, amount: data.amount, period }) } } } diff --git a/frontend/views/containers/payments/PaymentDetail.vue b/frontend/views/containers/payments/PaymentDetail.vue index e2c1b07106..8328e1aba6 100644 --- a/frontend/views/containers/payments/PaymentDetail.vue +++ b/frontend/views/containers/payments/PaymentDetail.vue @@ -27,13 +27,18 @@ modal-template(ref='modal' v-if='payment' :a11yTitle='L("Payment details")') i18n.has-text-1 Notes p.has-text-bold {{ payment.data.memo }} - .buttons.c-buttons-container(v-if='!lightningPayment') + .buttons.c-buttons-container( + v-if='!lightningPayment && buttonCount > 0' + :class='{ "is-centered": buttonCount === 1 }' + ) i18n.button.is-outlined( tag='button' + v-if='isPaidByMyself && !payment.isOldPayment' @click='cancelPayment' ) Cancel payment i18n.button( + v-if='!isPaidByMyself' tag='button' @click='sendThankYou' ) Send Thanks! @@ -48,7 +53,7 @@ import ModalTemplate from '@components/modal/ModalTemplate.vue' import LinkToCopy from '@components/LinkToCopy.vue' import PaymentsMixin from '@containers/payments/PaymentsMixin.js' import currencies from '@model/contracts/shared/currencies.js' -import { humanDate } from '@model/contracts/shared/time.js' +import { humanDate, comparePeriodStamps } from '@model/contracts/shared/time.js' import { cloneDeep } from '@model/contracts/shared/giLodash.js' export default ({ @@ -76,16 +81,25 @@ export default ({ ...mapGetters([ 'ourUsername', 'userDisplayName', - 'periodStampGivenDate' + 'periodStampGivenDate', + 'currentPaymentPeriod' ]), withCurrency () { return currencies[this.payment.data.currencyFromTo[1]].displayWithCurrency }, + fromUser () { + return this.payment?.meta.username || '' + }, + isPaidByMyself () { + return this.fromUser === this.ourUsername + }, + buttonCount () { + return Number(!this.isPaidByMyself) + Number(this.isPaidByMyself && !this.payment.isOldPayment) + }, subtitleCopy () { const toUser = this.payment.data.toUser - const fromUser = this.payment.meta.username const arg = (username) => ({ name: this.userDisplayName(username) }) - return toUser === this.ourUsername ? L('Sent by {name}', arg(fromUser)) : L('Sent to {name}', arg(toUser)) + return toUser === this.ourUsername ? L('Sent by {name}', arg(this.fromUser)) : L('Sent to {name}', arg(toUser)) } }, methods: { @@ -103,6 +117,7 @@ export default ({ this.payment = cloneDeep(payment) // TODO: the payment augmentation duplication in Payment and PaymentRecord, and between todo/sent/received, needs to be resolved more thoroughly this.payment.periodstamp = this.periodStampGivenDate(this.payment.meta.createdDate) + this.payment.isOldPayment = comparePeriodStamps(this.payment.periodstamp, this.currentPaymentPeriod) < 0 } else { console.warn('PaymentDetail: Missing valid query "id"') sbp('okTurtles.events/emit', CLOSE_MODAL) @@ -178,6 +193,10 @@ export default ({ margin: 1.625rem auto 0; width: 100%; + &.is-centered { + justify-content: center; + } + .button:not(:last-child) { margin-right: 0; } diff --git a/frontend/views/containers/payments/PaymentRowSent.vue b/frontend/views/containers/payments/PaymentRowSent.vue index 534c91daba..b2ecedf805 100644 --- a/frontend/views/containers/payments/PaymentRowSent.vue +++ b/frontend/views/containers/payments/PaymentRowSent.vue @@ -34,6 +34,7 @@ i18n Payment details menu-item( + v-if='!isOldPayment' tag='button' item-id='message' icon='times' @@ -49,7 +50,7 @@ import AvatarUser from '@components/AvatarUser.vue' import { OPEN_MODAL } from '@utils/events.js' import { MenuItem } from '@components/menu/index.js' import { PAYMENT_CANCELLED, PAYMENT_NOT_RECEIVED } from '@model/contracts/shared/payments/index.js' -import { humanDate } from '@model/contracts/shared/time.js' +import { humanDate, comparePeriodStamps } from '@model/contracts/shared/time.js' import PaymentRow from './payment-row/PaymentRow.vue' import PaymentActionsMenu from './payment-row/PaymentActionsMenu.vue' import PaymentNotReceivedTooltip from './payment-row/PaymentNotReceivedTooltip.vue' @@ -73,10 +74,15 @@ export default ({ ...mapGetters([ 'ourGroupProfile', 'withGroupCurrency', - 'periodStampGivenDate' + 'periodStampGivenDate', + 'currentPaymentPeriod' ]), notReceived () { return this.payment.data.status === PAYMENT_NOT_RECEIVED + }, + isOldPayment () { + // check if it's a past transaction item. + return comparePeriodStamps(this.payment.period, this.currentPaymentPeriod) < 0 } }, methods: { diff --git a/frontend/views/containers/payments/PaymentsMixin.js b/frontend/views/containers/payments/PaymentsMixin.js index 85d388bab9..07be33b48e 100644 --- a/frontend/views/containers/payments/PaymentsMixin.js +++ b/frontend/views/containers/payments/PaymentsMixin.js @@ -41,7 +41,7 @@ const PaymentsMixin: Object = { for (const hash of paymentsFrom[fromUser][toUser]) { if (hash in payments) { const { data, meta } = payments[hash] - paymentsInTypes[receivedOrSent].push({ hash, data, meta, amount: data.amount, username: toUser }) + paymentsInTypes[receivedOrSent].push({ hash, data, meta, amount: data.amount, username: toUser, period }) } else { console.error(`getHistoricalPaymentsInTypes: couldn't find payment ${hash} for period ${period}!`) } @@ -89,6 +89,7 @@ const PaymentsMixin: Object = { async getHistoricalPaymentByHashAndPeriod (hash: string, period: string) { const paymentsKey = `payments/${this.ourUsername}/${period}/${this.currentGroupId}` const payments = await sbp('gi.db/archive/load', paymentsKey) || {} + return payments[hash] }, async getHaveNeedsSnapshotByPeriod (period: string) { diff --git a/frontend/views/pages/Payments.vue b/frontend/views/pages/Payments.vue index 56347cc120..be568f3179 100644 --- a/frontend/views/pages/Payments.vue +++ b/frontend/views/pages/Payments.vue @@ -226,7 +226,14 @@ export default ({ if (section && this.tabSections.includes(section)) { this.ephemeral.activeTab = section } else { - const defaultTab = this.tabSections[0] + const fromQuery = from?.query || {} + const isFromPaymentDetailModal = fromQuery.modal === 'PaymentDetail' + const defaultTab = isFromPaymentDetailModal + // When payment detail modal is closed, the payment table has to remain in the previously active tab. + // (context: https://github.com/okTurtles/group-income/issues/1686) + ? fromQuery.section || this.tabSections[0] + : this.tabSections[0] + if (defaultTab) { this.handleTabClick(defaultTab) } else if (section) { @@ -290,16 +297,17 @@ export default ({ return items }, tableTitles () { - const firstTab = this.needsIncome ? L('Sent by') : L('Sent to') - return this.ephemeral.activeTab === 'PaymentRowTodo' + const { activeTab } = this.ephemeral + + return activeTab === 'PaymentRowTodo' ? { - one: firstTab, + one: L('Sent to'), two: L('Amount'), three: L('Accepted methods'), four: L('Due on') } : { - one: firstTab, + one: activeTab === 'PaymentRowSent' ? L('Sent to') : L('Sent by'), two: L('Amount'), three: L('Payment method'), four: L('Payment date')