Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add a new page component for redirecting disputes => details: #7310

Merged
merged 32 commits into from
Oct 3, 2023

Conversation

haszari
Copy link
Contributor

@haszari haszari commented Sep 27, 2023

Fixes #6891

This is ready for review though I haven't fully updated the PR description. Reviewer(s) please ping me / review harshly if any info is missing or not clear!

This PR changes behaviour with _wcpay_feature_dispute_on_transaction_page enabled, and should preserve existing behaviour with flag off or missing.

  • registers a new handler component for dispute details - RedirectToTransactionDetails
  • that handler generates the relevant url and redirects there
  • uses a notice and spinner component to reassure the user while the redirect happens. This takes 2-10 seconds. In my testing this delay only applies to first nav to that specific dispute. I'm assuming there's some caching somewhere.
Screenshot 2023-09-29 at 11 44 58 AM
Screen.Recording.2023-09-29.at.11.47.06.AM.mov

Still to do:

  • get the redirect working
  • get the user experience working smoothly
    • currently takes time to generate the URL
    • we may need an interstitial "one moment please" UI
  • ensure works correctly with feature flag _wcpay_feature_dispute_on_transaction_page on/off
  • make it robust – we dig deep into dispute object for required url params, so we need to fail gracefully if anything's missing
    • I don't think there are any blockers here - look out when reviewing please

Testing instructions

  • As shopper, purchase products with dispute cards so merchant has disputed payments.
  • Test navigating to dispute list view and clicking disputes with feature flag on and off.

Feature flag: _wcpay_feature_dispute_on_transaction_page

e.g. set with WP CLI docker-compose exec wordpress wp --allow-root option set _wcpay_feature_dispute_on_transaction_page 1

_wcpay_feature_dispute_on_transaction_page enabled
  • Clicking any dispute should ultimately land on relevant transaction details page, including info about the dispute.
  • You may see an interstitial redirect progress UI in the middle.
_wcpay_feature_dispute_on_transaction_page disabled or missing
  • Clicking any dispute should link direct to the relevant (legacy) dispute detail page.

  • Run npm run changelog to add a changelog file, choose patch to leave it empty if the change is not significant. You can add multiple changelog files in one PR by running this command a few times.
  • Covered with tests (or have a good reason not to test in description ☝️)
  • Tested on mobile (or does not apply)

Post merge

- first cut
- registers a new handler component for dispute details
 - RedirectToTransactionDetails
- that handler generates the relevant url

Still to do:
- get the redirect working
- get the user experience working smoothly
- currently takes time to generate the URL
- we may need an "interstitial" "one moment please" UI
@haszari haszari self-assigned this Sep 27, 2023
@haszari
Copy link
Contributor Author

haszari commented Sep 27, 2023

I'm in the middle of this but I have no objections to anyone taking over at any time 😁

@botwoo
Copy link
Collaborator

botwoo commented Sep 27, 2023

Test the build

Option 1. Jetpack Beta

  • Install and activate Jetpack Beta.
  • Use this build by searching for PR number 7310 or branch name fix/6891-redirect-dispute-detail-to-transaction in your-test.site/wp-admin/admin.php?page=jetpack-beta&plugin=woocommerce-payments

Option 2. Jurassic Ninja - available for logged-in A12s

🚀 Launch a JN site with this branch 🚀

ℹ️ Install this Tampermonkey script to get more options.


Build info:

  • Latest commit: 6216ae0
  • Build time: 2023-10-03 22:43:17 UTC

Note: the build is updated when a new commit is pushed to this PR.

@github-actions
Copy link
Contributor

github-actions bot commented Sep 27, 2023

Size Change: +1.37 kB (0%)

Total Size: 1.42 MB

Filename Size Change
release/woocommerce-payments/dist/index-rtl.css 36.4 kB +31 B (0%)
release/woocommerce-payments/dist/index.css 36.4 kB +31 B (0%)
release/woocommerce-payments/dist/index.js 283 kB +987 B (0%)
release/woocommerce-payments/dist/multi-currency-switcher-block.js 60.2 kB +67 B (0%)
release/woocommerce-payments/dist/multi-currency.js 54.8 kB +49 B (0%)
release/woocommerce-payments/dist/order.js 41 kB +86 B (0%)
release/woocommerce-payments/dist/payment-gateways.js 38.4 kB +65 B (0%)
release/woocommerce-payments/dist/settings.js 232 kB +53 B (0%)
ℹ️ View Unchanged
Filename Size
release/woocommerce-payments/assets/css/admin.css 1.03 kB
release/woocommerce-payments/assets/css/success.css 158 B
release/woocommerce-payments/dist/blocks-checkout-rtl.css 1.51 kB
release/woocommerce-payments/dist/blocks-checkout.css 1.51 kB
release/woocommerce-payments/dist/blocks-checkout.js 74.5 kB
release/woocommerce-payments/dist/checkout-rtl.css 440 B
release/woocommerce-payments/dist/checkout.css 441 B
release/woocommerce-payments/dist/checkout.js 28.8 kB
release/woocommerce-payments/dist/multi-currency-analytics.js 1.05 kB
release/woocommerce-payments/dist/multi-currency-rtl.css 2.88 kB
release/woocommerce-payments/dist/multi-currency.css 2.88 kB
release/woocommerce-payments/dist/order-rtl.css 676 B
release/woocommerce-payments/dist/order.css 679 B
release/woocommerce-payments/dist/payment-gateways-rtl.css 690 B
release/woocommerce-payments/dist/payment-gateways.css 692 B
release/woocommerce-payments/dist/payment-request-rtl.css 146 B
release/woocommerce-payments/dist/payment-request.css 146 B
release/woocommerce-payments/dist/payment-request.js 11.8 kB
release/woocommerce-payments/dist/product-details.js 898 B
release/woocommerce-payments/dist/settings-rtl.css 8.92 kB
release/woocommerce-payments/dist/settings.css 8.92 kB
release/woocommerce-payments/dist/subscription-edit-page.js 669 B
release/woocommerce-payments/dist/subscription-product-onboarding-modal-rtl.css 519 B
release/woocommerce-payments/dist/subscription-product-onboarding-modal.css 519 B
release/woocommerce-payments/dist/subscription-product-onboarding-modal.js 20.3 kB
release/woocommerce-payments/dist/subscription-product-onboarding-toast.js 693 B
release/woocommerce-payments/dist/subscriptions-empty-state-rtl.css 117 B
release/woocommerce-payments/dist/subscriptions-empty-state.css 117 B
release/woocommerce-payments/dist/subscriptions-empty-state.js 19.4 kB
release/woocommerce-payments/dist/tos-rtl.css 230 B
release/woocommerce-payments/dist/tos.css 231 B
release/woocommerce-payments/dist/tos.js 21.9 kB
release/woocommerce-payments/dist/upe_checkout-rtl.css 440 B
release/woocommerce-payments/dist/upe_checkout.css 441 B
release/woocommerce-payments/dist/upe_checkout.js 34.1 kB
release/woocommerce-payments/dist/upe_split_checkout-rtl.css 440 B
release/woocommerce-payments/dist/upe_split_checkout.css 441 B
release/woocommerce-payments/dist/upe_split_checkout.js 34.6 kB
release/woocommerce-payments/dist/upe_with_deferred_intent_creation_checkout.js 36.7 kB
release/woocommerce-payments/dist/upe-blocks-checkout-rtl.css 1.51 kB
release/woocommerce-payments/dist/upe-blocks-checkout.css 1.51 kB
release/woocommerce-payments/dist/upe-blocks-checkout.js 39.6 kB
release/woocommerce-payments/dist/upe-split-blocks-checkout-rtl.css 1.51 kB
release/woocommerce-payments/dist/upe-split-blocks-checkout.css 1.51 kB
release/woocommerce-payments/dist/upe-split-blocks-checkout.js 41 kB
release/woocommerce-payments/dist/woopay-express-button-rtl.css 146 B
release/woocommerce-payments/dist/woopay-express-button.css 146 B
release/woocommerce-payments/dist/woopay-express-button.js 51.5 kB
release/woocommerce-payments/dist/woopay-rtl.css 3.85 kB
release/woocommerce-payments/dist/woopay.css 3.85 kB
release/woocommerce-payments/dist/woopay.js 71.5 kB
release/woocommerce-payments/includes/subscriptions/assets/css/plugin-page.css 633 B
release/woocommerce-payments/includes/subscriptions/assets/js/plugin-page.js 720 B
release/woocommerce-payments/vendor/automattic/jetpack-assets/build/i18n-loader.js 2.43 kB
release/woocommerce-payments/vendor/automattic/jetpack-assets/src/js/i18n-loader.js 1.01 kB
release/woocommerce-payments/vendor/automattic/jetpack-connection/dist/tracks-ajax.js 522 B
release/woocommerce-payments/vendor/automattic/jetpack-connection/dist/tracks-callables.js 581 B
release/woocommerce-payments/vendor/automattic/jetpack-identity-crisis/babel.config.js 160 B
release/woocommerce-payments/vendor/automattic/jetpack-identity-crisis/build/index.css 2.32 kB
release/woocommerce-payments/vendor/automattic/jetpack-identity-crisis/build/index.js 13.8 kB
release/woocommerce-payments/vendor/automattic/jetpack-identity-crisis/build/index.rtl.css 2.32 kB
release/woocommerce-payments/vendor/woocommerce/subscriptions-core/assets/css/about.css 1.2 kB
release/woocommerce-payments/vendor/woocommerce/subscriptions-core/assets/css/admin-empty-state.css 291 B
release/woocommerce-payments/vendor/woocommerce/subscriptions-core/assets/css/admin-order-statuses.css 403 B
release/woocommerce-payments/vendor/woocommerce/subscriptions-core/assets/css/admin.css 3.56 kB
release/woocommerce-payments/vendor/woocommerce/subscriptions-core/assets/css/checkout.css 299 B
release/woocommerce-payments/vendor/woocommerce/subscriptions-core/assets/css/modal.css 742 B
release/woocommerce-payments/vendor/woocommerce/subscriptions-core/assets/css/view-subscription.css 572 B
release/woocommerce-payments/vendor/woocommerce/subscriptions-core/assets/css/wcs-upgrade.css 411 B
release/woocommerce-payments/vendor/woocommerce/subscriptions-core/assets/js/admin/admin-pointers.js 544 B
release/woocommerce-payments/vendor/woocommerce/subscriptions-core/assets/js/admin/admin.js 9.63 kB
release/woocommerce-payments/vendor/woocommerce/subscriptions-core/assets/js/admin/jstz.js 6.8 kB
release/woocommerce-payments/vendor/woocommerce/subscriptions-core/assets/js/admin/jstz.min.js 3.83 kB
release/woocommerce-payments/vendor/woocommerce/subscriptions-core/assets/js/admin/meta-boxes-coupon.js 544 B
release/woocommerce-payments/vendor/woocommerce/subscriptions-core/assets/js/admin/meta-boxes-subscription.js 2.38 kB
release/woocommerce-payments/vendor/woocommerce/subscriptions-core/assets/js/admin/moment.js 22.1 kB
release/woocommerce-payments/vendor/woocommerce/subscriptions-core/assets/js/admin/moment.min.js 11.6 kB
release/woocommerce-payments/vendor/woocommerce/subscriptions-core/assets/js/admin/payment-method-restrictions.js 1.29 kB
release/woocommerce-payments/vendor/woocommerce/subscriptions-core/assets/js/admin/wcs-meta-boxes-order.js 502 B
release/woocommerce-payments/vendor/woocommerce/subscriptions-core/assets/js/frontend/payment-methods.js 355 B
release/woocommerce-payments/vendor/woocommerce/subscriptions-core/assets/js/frontend/single-product.js 429 B
release/woocommerce-payments/vendor/woocommerce/subscriptions-core/assets/js/frontend/view-subscription.js 1.38 kB
release/woocommerce-payments/vendor/woocommerce/subscriptions-core/assets/js/frontend/wcs-cart.js 781 B
release/woocommerce-payments/vendor/woocommerce/subscriptions-core/assets/js/modal.js 1.1 kB
release/woocommerce-payments/vendor/woocommerce/subscriptions-core/assets/js/wcs-upgrade.js 1.27 kB
release/woocommerce-payments/vendor/woocommerce/subscriptions-core/build/index.css 392 B
release/woocommerce-payments/vendor/woocommerce/subscriptions-core/build/index.js 3.06 kB

compressed-size-action

const { dispute, isLoading } = useDispute( disputeId );
const disputeObject = dispute || ( {} as Dispute );
const disputeIsAvailable = ! isLoading && dispute && disputeObject.id;
// Why would dispute.charge ever be a string?
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Our interfaces for Disputes are not 100% accurate here.:

  • When fetching a charge, charge.dispute.charge: string
  • When fetching a dispute, dispute.charge: Charge

Currently, we use Dispute as the interface for both of these response types.

I've had a first crack at solving this in #7311 – unsure if it's the right way to go.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the tip - will take a look!

Copy link
Contributor Author

@haszari haszari Sep 28, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added some comments to the PR. This feels a bit like typescript chicanery at first glance. Hoping there's a simple non-hack solution! If we need to start sketching out hierarchical types that implies maintenance risk IMO (object oriented vibes).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've updated my comment, since the type is correct in my view. The part I don't understand – is casting the correct approach an "OR" type like this? I.e. let me know if as Charge is a hack, or if I need a runtime type check.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see type-casting as a risk in this scenario since it makes assumptions about the code that may not always be true. E.g. we may in the future change the response type of this endpoint to always return dispute.charge: string and not realise that this has been overridden (as Charge) throughout the codebase.

I think a more robust way to assert that dispute.charge === Charge would be via a type guard function e.g.:

const isCharge = ( charge: Charge | string ): charge is Charge => {
	return typeof charge !== 'string';
};
const { charge } = dispute;
if ( isCharge( charge ) ) {
	// charge.payment_intent exists!

However, since we know that GET disputes/{id} endpoint will return a dispute with dispute.charge: Charge, it would be better to fix this upstream (the Dispute interface) since we have full control here.

I think it's OK to follow this up in a future PR, however.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks - I look forward to understanding this better in the upstream PR.

This kind of thing feels very heavy compared to classic loose-duck-types JavaScript, so I want to really understand how to use TypeScript to strengthen things in a lean/elegant way.

Rua Haszard and others added 2 commits September 28, 2023 13:44
@haszari
Copy link
Contributor Author

haszari commented Sep 28, 2023

get the user experience working smoothly
(currently takes time to generate the URL - we may need an interstitial "one moment please" UI)

@nikkivias do you know how we handle things like this?

  • When the merchant lands on the old (legacy) dispute details page, we're going to redirect them to the transaction details page.
  • The redirect takes a moment or two (seconds) so the dispute details page, or some page/UI, will flash up briefly.
  • We need that UI to bridge the gap between what they clicked and where they will end up (transaction details) – i.e. interstitial UI.

I'm thinking we need a message like Loading transaction… or Redirecting to transaction… with a spinner of some sort.

@nikkivias
Copy link

nikkivias commented Sep 28, 2023

I will look into how we might handle this @haszari how many seconds is it currently taking to redirect?

@haszari
Copy link
Contributor Author

haszari commented Sep 28, 2023

how many seconds is it currently taking to redirect?

Good question @nikkivias! 2-5 seconds approximately. Here's a video :)

Screen.Recording.2023-09-28.at.5.50.59.PM.mov

@Jinksi

This comment was marked as outdated.

@haszari
Copy link
Contributor Author

haszari commented Sep 28, 2023

FYI @nikkivias there's an updated prototype now using standard <Notice><Spinner> components. See video in PR description.

I think this is almost shippable, especially since this UI will be very rarely seen (only on first nav to each unique dispute, and we are planning on fixing most links anyway). The text and spinner alignment needs tweaking though - now looking good using Flex components.

Let me know what you think.

Rua Haszard added 5 commits September 29, 2023 11:46
- feature off: render (legacy) dispute details page
- feature on: render a redirect to transaction details
- This module should be deleted when we go live with this feature
- Adding this as a reminder
@@ -27,11 +27,19 @@ const DisputeDetails = ( {
query: { id: disputeId },
}: {
query: { id: string };
} ): JSX.Element => {
} ): JSX.Element | null => {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm (now) in the habit of declaring React components as Element | null, consistent with classic React. As I understand, returning null is totally fine way to not render anything.

Question for front end experts – is this a good approach? Should we have a standard type for this? What does the TypeScript React community do?

Copy link
Contributor

@shendy-a8c shendy-a8c Sep 29, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not a frontend expert but since as of currently, there is a possibility this component will return null, this | null addition is necessary.

However, I have my opinion that checking for feature flag and returning null is not necessary here. Read along =)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that when we use React.FC/React.FunctionComponent, the return type is implicit and we don't have to define it (see TS guidelines doc).

I've switched this to React.FC in 2c4dbfc.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Aha! Now I'm a fan of React.FC and will use from here on in. Can we encourage that pattern with a linter or tooling?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking at the doc, we might be able to distil this to a principle:

  • Use React.FC type for React components

<FlexItem>
<span>Redirecting to payment details…</span>
</FlexItem>
</Flex>
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Happy days using core components to get a nice, consistent look out of the box!

if ( isDisputeOnTransactionPageEnabled ) {
return null;
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added this so when we're working on #7289 we find this class (search for feature flag) and remember to delete it. 🐘

Copy link
Contributor

@shendy-a8c shendy-a8c Sep 29, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To be explicit, I just added one check item in #7289:

@haszari haszari marked this pull request as ready for review September 28, 2023 23:48
@Jinksi
Copy link
Member

Jinksi commented Oct 3, 2023

What happens if dispute id is invalid? For example /wp-admin/admin.php?page=wc-admin&path=%2Fpayments%2Fdisputes%2Fdetails&id=dp_abc.

In addition, we aren't handling network errors, see screen recording:

Dispute.redirect.network.error.mov

I'm looking into fixing this. PS, this wasn't caught by TS because we're currently casting dispute: Dispute as a return value of useDispute, whereas it is actually dispute: Dispute | undefined

<div>
<b>Error retrieving dispute</b>
</div>
<div>Please check your network and try again.</div>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could consider giving more accurate error messages, e.g. looking at error.code to determine a suitable messages for resource_missing and fetch_error.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not needed IMO, this redirect is a fallback so will hopefully not get much use. If merchant lands here and it doesn't work for any reason, they should be able to get back on track :)

@Jinksi
Copy link
Member

Jinksi commented Oct 3, 2023

I've handled network errors in bec0846, 5e006f8, 6111fac, 6b00887.

See screen recording example of using an unknown dispute id below.

Dispute.redirect.network.error.handled.mov

Another edge case that may or may not require investigating – what will happen if a live-mode dispute email is clicked and the site is in test-mode?

Copy link
Member

@Jinksi Jinksi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This tests well @haszari 🙌

I've made some relatively significant additions to this PR, getDispute error handling in particular – I've approved this PR on the assumption that you're happy with the changes I've introduced 😅

@@ -44,7 +49,7 @@ export const useDispute = (
const { acceptDispute } = useDispatch( STORE_NAME );
const doAccept = () => acceptDispute( id );

return { dispute, isLoading, doAccept };
return { dispute, isLoading, error, doAccept };
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding support for handling dispute load errors is possibly overengineering for this use case (fallback redirect), but is useful in general so let's keep it :)

I do wonder how we got to this point in the project without adding this!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The resolver shows a snackbar on error: “Error retrieving dispute”. I’d guess that this has been a sufficient way to handle errors until now.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, so is there a chance we don't need this extra support? Happy for it to merge, just clarifying the value.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep the value is low at the moment, we’re only checking for a fetch error in this component. Alternatively, we could move the error handling to the component, but I think it’s more predictable to handle dispute fetch errors in a way that is consistent with other wp.data resolvers.

@@ -68,6 +69,9 @@ describe( 'getDispute resolver', () => {
expect.any( String )
)
);
expect( generator.next().value ).toEqual(
updateErrorForDispute( 'dp_mock1', undefined, errorResponse )
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Curious what regressions tests like this would catch.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess it could help ensure that we’re calling the update function in the case of a refactor but it feels a bit like we’re testing implementation details here.

Note that this isn’t a new test, I added this here to fix a failing test after adding updateErrorForDispute to the getDispute resolver.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gotcha, figured there would be a thread pulling you here 😁

} ) => {
const { dispute, error, isLoading } = useDispute( disputeId );

useEffect( () => {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doh! Is this the fix for my react issue! Obvious in hindsight that I would need to redirect as a side effect. Thanks for sorting this @Jinksi , major oversight on my side.

@haszari
Copy link
Contributor Author

haszari commented Oct 3, 2023

Thank you @Jinksi for getting this PR shipshape. Especially for useEffect & fixing the react console error 🤦

Another edge case that may or may not require investigating – what will happen if a live-mode dispute email is clicked and the site is in test-mode?

I don't think we need to worry about any more edge cases, the only real goal here is that if merchant navs to a dispute, they get redirected to the correct transaction. If there are issues that mean we can't redirect, we have to trust that the merchant will be able to use the UI to get back on track – loading this component with low-risk contingencies is not worth the effort IMO.

@haszari
Copy link
Contributor Author

haszari commented Oct 3, 2023

Merging!

@haszari haszari enabled auto-merge October 3, 2023 22:45
@haszari haszari dismissed shendy-a8c’s stale review October 3, 2023 22:46

Changes are addressed and has been re-reviewed by @Jinksi

@haszari haszari added this pull request to the merge queue Oct 3, 2023
Merged via the queue into develop with commit 1e696f8 Oct 3, 2023
26 of 27 checks passed
@haszari haszari deleted the fix/6891-redirect-dispute-detail-to-transaction branch October 3, 2023 23:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Redirect legacy dispute details URL(s) to transaction details screen
5 participants