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 718f6f334f..015823ec3c 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 ({ @@ -80,11 +85,19 @@ export default ({ 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: { @@ -104,6 +117,7 @@ export default ({ ...cloneDeep(payment), periodstamp: await this.getPeriodStampGivenDate(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) @@ -179,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 1cb4f99b15..9b567206dc 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' @@ -78,6 +79,10 @@ export default ({ ]), 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 } }, created () { diff --git a/frontend/views/containers/payments/PaymentsMixin.js b/frontend/views/containers/payments/PaymentsMixin.js index bb4206c49d..47b8b39dd1 100644 --- a/frontend/views/containers/payments/PaymentsMixin.js +++ b/frontend/views/containers/payments/PaymentsMixin.js @@ -102,7 +102,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}!`) } @@ -150,6 +150,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 845abed3c0..50c8acbd59 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) { @@ -289,16 +296,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')