Skip to content

Commit

Permalink
Merge commit '8a789df662dfa582adbf38f59afaf6ad399da79a' into update-v…
Browse files Browse the repository at this point in the history
…8.4.0-from-upstream
  • Loading branch information
Gnito committed Dec 2, 2021
2 parents ce0ee99 + 8a789df commit ed2c066
Show file tree
Hide file tree
Showing 15 changed files with 1,103 additions and 612 deletions.
33 changes: 33 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ https://github.com/sharetribe/flex-template-web/

## Upcoming version 2021-XX-XX

## [v10.4.0] 2021-12-02

- [fix] When changing calendar month, the current month was not set correctly. (Selecting date
closes the react-dates calendar and today was saved instead of currently selected one.)
[#176](https://github.com/sharetribe/ftw-hourly/pull/176)
Expand All @@ -35,6 +37,37 @@ https://github.com/sharetribe/flex-template-web/

[#171](https://github.com/sharetribe/ftw-hourly/pull/171)

### Updates from upstream (FTW-daily v8.4.0)

- [fix] Mobile safari zooms if input-related elements have smaller font-size than 16px. This updates
textarea and select element styles too. [#1489](https://github.com/sharetribe/ftw-daily/pull/1489)
- [fix] Dependabot: Bump passport-oauth2 from 1.5.0 to 1.6.1
[#1487](https://github.com/sharetribe/ftw-daily/pull/1487)
- [fix] Fix bugs in checkout process:

- Submit button was enabled prematurely for onetime payments
- Toggling between default card and onetime payment flows was not working correctly in case of
error (e.g. network error).
- Calling Stripe.confirmCardPayment when status is requires_capture is unnecessary.

[#1486](https://github.com/sharetribe/ftw-daily/pull/1486)

- [change] Update many dependencies. See full list in the package.json changes in the PR.
[#1483](https://github.com/sharetribe/ftw-daily/pull/1483)
- [fix] Double click issue. Show dedicated message, when current user doesn't have a pending email
address, but there's a verification error.
[#1485](https://github.com/sharetribe/ftw-daily/pull/1485)
- [change] Update comment about how scrollIntoView works with links using hash.
[#1484](https://github.com/sharetribe/ftw-daily/pull/1484)
- [fix] Account pages: mobile tab navigation should only scroll horizontally
[#1481](https://github.com/sharetribe/ftw-daily/pull/1481)
- [fix] Temporarily disallow Node v17, since it causes issues with dependencies.
[#1479](https://github.com/sharetribe/ftw-daily/pull/1479)
- [fix] Fix modal close button text/icon alignment
[#1476](https://github.com/sharetribe/ftw-daily/pull/1476)

[v10.4.0]: https://github.com/sharetribe/ftw-hourly/compare/v10.3.0...v10.4.0

## [v10.3.0] 2021-09-22

- [fix] EditListingPage: fix an old bug (before we had draft-listing state the update flow was
Expand Down
12 changes: 8 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,21 @@

[![CircleCI](https://circleci.com/gh/sharetribe/ftw-hourly.svg?style=svg)](https://circleci.com/gh/sharetribe/ftw-hourly)

This is a template web application for a Sharetribe Flex marketplace ready to be extended and
customized. It is based on an application bootstrapped with
[create-react-app](https://github.com/facebookincubator/create-react-app) with some additions,
namely server side rendering and a custom CSS setup.

> Note: We also have two more templates available:
> [FTW-daily](https://github.com/sharetribe/ftw-daily) and
> [FTW-product](https://github.com/sharetribe/ftw-product). FTW-daily focuses on day-based booking
> processes. You can [get it from GitHub](https://github.com/sharetribe/ftw-daily). FTW-product
> focuses on product marketplace with listing stock management. You can find more information in the
> [introduction to FTW-product Flex Docs](https://www.sharetribe.com/docs/ftw-introduction/ftw-product/).
This is a template web application for a Sharetribe Flex marketplace using **time-based booking
process**. This repository is a fork from the existing
[FTW repository](https://github.com/sharetribe/ftw-hourly) (which is using nightly booking process).
So, if you have started working with the original FTW repository and you want to change to
This template is using **time-based booking process**. This repository is a fork from the original
[FTW-daily repository](https://github.com/sharetribe/ftw-daily) (which is using nightly booking
process). So, if you have started working with the original FTW repository and you want to change to
FTW-hourly, you can
[change the 'upstream' remote repository](https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/configuring-a-remote-for-a-fork).
You can read more about time-based process from the related
Expand Down
80 changes: 40 additions & 40 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,81 +1,81 @@
{
"name": "app",
"version": "10.3.0",
"version": "10.4.0",
"private": true,
"license": "Apache-2.0",
"dependencies": {
"@babel/runtime": "^7.11.2",
"@formatjs/intl-relativetimeformat": "^4.2.1",
"@babel/runtime": "^7.16.3",
"@formatjs/intl-relativetimeformat": "^9.3.2",
"@loadable/component": "^5.14.1",
"@loadable/server": "^5.14.2",
"@mapbox/polyline": "^1.1.1",
"@sentry/browser": "5.20.1",
"@sentry/node": "5.20.1",
"array-includes": "^3.1.3",
"array.prototype.find": "^2.1.1",
"autosize": "^4.0.0",
"@sentry/browser": "^6.15.0",
"@sentry/node": "^6.15.0",
"array-includes": "^3.1.4",
"array.prototype.find": "^2.1.2",
"autosize": "^5.0.1",
"basic-auth": "^2.0.1",
"body-parser": "^1.18.3",
"classnames": "^2.3.1",
"compression": "^1.7.4",
"cookie-parser": "^1.4.5",
"core-js": "^3.6.5",
"cookie-parser": "^1.4.6",
"core-js": "^3.19.2",
"cors": "^2.8.5",
"decimal.js": "10.2.0",
"dotenv": "6.2.0",
"dotenv-expand": "4.2.0",
"decimal.js": "^10.3.1",
"dotenv": "8.2.0",
"dotenv-expand": "5.1.0",
"express": "^4.16.4",
"express-enforces-ssl": "^1.1.0",
"express-sitemap": "^1.8.0",
"final-form": "^4.20.2",
"final-form": "^4.20.4",
"final-form-arrays": "^3.0.2",
"full-icu": "^1.3.4",
"helmet": "^4.0.0",
"intl-pluralrules": "^1.3.0",
"full-icu": "^1.4.0",
"helmet": "^4.6.0",
"intl-pluralrules": "^1.3.1",
"jose": "3.11.4",
"jstimezonedetect": "^1.0.7",
"lodash": "^4.17.21",
"mapbox-gl-multitouch": "^1.0.3",
"moment": "^2.22.2",
"moment-timezone": "^0.5.26",
"object.entries": "^1.1.4",
"object.values": "^1.1.4",
"passport": "^0.4.1",
"moment": "^2.29.1",
"moment-timezone": "^0.5.34",
"object.entries": "^1.1.5",
"object.values": "^1.1.5",
"passport": "^0.5.0",
"passport-facebook": "^3.0.0",
"passport-google-oauth": "^2.0.0",
"path-to-regexp": "^6.1.0",
"path-to-regexp": "^6.2.0",
"prop-types": "^15.7.2",
"query-string": "^6.13.1",
"query-string": "^7.0.1",
"raf": "^3.4.0",
"react": "^16.13.1",
"react-dates": "^21.3.1",
"react-dates": "^21.8.0",
"react-dom": "^16.13.1",
"react-final-form": "^6.5.3",
"react-final-form": "^6.5.7",
"react-final-form-arrays": "^3.1.3",
"react-helmet-async": "^1.0.6",
"react-intl": "^5.20.10",
"react-moment-proptypes": "^1.6.0",
"react-redux": "^7.2.1",
"react-helmet-async": "^1.1.2",
"react-intl": "^5.22.0",
"react-moment-proptypes": "^1.8.1",
"react-redux": "^7.2.6",
"react-router-dom": "^5.2.0",
"react-with-direction": "^1.3.1",
"redux": "^4.0.5",
"redux-thunk": "^2.3.0",
"react-with-direction": "^1.4.0",
"redux": "^4.1.2",
"redux-thunk": "^2.4.1",
"seedrandom": "^3.0.5",
"sharetribe-flex-sdk": "1.13.0",
"sharetribe-flex-sdk": "^1.15.0",
"sharetribe-scripts": "5.0.1",
"smoothscroll-polyfill": "^0.4.0",
"source-map-support": "^0.5.9",
"source-map-support": "^0.5.21",
"url": "^0.11.0"
},
"devDependencies": {
"bfj": "^7.0.2",
"chalk": "^v4.1.2",
"concurrently": "^5.3.0",
"concurrently": "^6.4.0",
"enzyme": "^3.11.0",
"enzyme-adapter-react-16": "^1.15.2",
"enzyme-to-json": "^3.5.0",
"inquirer": "^7.3.3",
"nodemon": "^2.0.12",
"enzyme-adapter-react-16": "^1.15.6",
"enzyme-to-json": "^3.6.2",
"inquirer": "^8.2.0",
"nodemon": "^2.0.15",
"prettier": "^1.18.2"
},
"resolutions": {
Expand Down
17 changes: 8 additions & 9 deletions src/Routes.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,16 +45,15 @@ const setPageScrollPosition = location => {
} else {
const el = document.querySelector(location.hash);
if (el) {
// Found element with the given fragment identifier, scrolling
// to that element.
// Found element from the current page with the given fragment identifier,
// scrolling to that element.
//
// NOTE: This isn't foolproof. It works when navigating within
// the application between pages and within a single page. It
// also works with the initial page load. However, it doesn't
// seem work work properly when refreshing the page, at least
// not in Chrome.
//
// TODO: investigate why the scrolling fails on refresh
// NOTE: This only works on in-app navigation within the same page.
// If smooth scrolling is needed between different pages, one needs to wait
// 1. loadData fetch and
// 2. code-chunk fetch
// before making el.scrollIntoView call.

el.scrollIntoView({
block: 'start',
behavior: 'smooth',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,45 +2,83 @@
* This is a wrapper component for different Layouts.
* Navigational 'aside' content should be added to this wrapper.
*/
import React from 'react';
import React, { useEffect } from 'react';
import { node, number, string, shape } from 'prop-types';
import { compose } from 'redux';

import { FormattedMessage } from '../../util/reactIntl';
import { withViewport } from '../../util/contextHelpers';
import { LayoutWrapperSideNav } from '../../components';

import { createGlobalState } from './hookGlobalState';

const MAX_HORIZONTAL_NAV_SCREEN_WIDTH = 1023;

const scrollToTab = currentTab => {
// Add global state for tab scrolling effect
const initialScrollState = { scrollLeft: 0 };
const { useGlobalState } = createGlobalState(initialScrollState);

// Horizontal scroll animation using element.scrollTo()
const scrollToTab = (currentTab, scrollLeft, setScrollLeft) => {
const el = document.querySelector(`#${currentTab}Tab`);

if (el) {
el.scrollIntoView({
block: 'end',
inline: 'end',
behavior: 'smooth',
});
// el.scrollIntoView doesn't work with Safari and it considers vertical positioning too.
// This scroll behaviour affects horizontal scrolling only
// and it expects that the immediate parent element is scrollable.
const parent = el.parentElement;
const parentRect = parent.getBoundingClientRect();
const maxScrollDistance = parent.scrollWidth - parentRect.width;

const hasParentScrolled = parent.scrollLeft > 0;
const scrollPositionCurrent = hasParentScrolled ? parent.scrollLeft : scrollLeft;

const tabRect = el.getBoundingClientRect();
const diffLeftBetweenTabAndParent = tabRect.left - parentRect.left;
const tabScrollPosition = parent.scrollLeft + diffLeftBetweenTabAndParent;

const scrollPositionNew =
tabScrollPosition > maxScrollDistance
? maxScrollDistance
: parent.scrollLeft + diffLeftBetweenTabAndParent;

const needsSmoothScroll = scrollPositionCurrent !== scrollPositionNew;

if (!hasParentScrolled || (hasParentScrolled && needsSmoothScroll)) {
// Ensure that smooth scroll animation uses old position as starting point after navigation.
parent.scrollTo({ left: scrollPositionCurrent });
// Scroll to new position
parent.scrollTo({ left: scrollPositionNew, behavior: 'smooth' });
}
// Always keep track of new position (even if smooth scrolling is not applied)
setScrollLeft(scrollPositionNew);
}
};

const LayoutWrapperAccountSettingsSideNavComponent = props => {
const { currentTab, viewport } = props;

let hasScrolledToTab = false;

const { width } = viewport;
const hasViewport = width > 0;
const hasHorizontalTabLayout = hasViewport && width <= MAX_HORIZONTAL_NAV_SCREEN_WIDTH;
const hasVerticalTabLayout = hasViewport && width > MAX_HORIZONTAL_NAV_SCREEN_WIDTH;
const hasFontsLoaded = hasViewport && document.documentElement.classList.contains('fontsLoaded');

// Check if scrollToTab call is needed (tab is not visible on mobile)
if (hasVerticalTabLayout) {
hasScrolledToTab = true;
} else if (hasHorizontalTabLayout && !hasScrolledToTab && hasFontsLoaded) {
scrollToTab(currentTab);
hasScrolledToTab = true;
}
const [scrollLeft, setScrollLeft] = useGlobalState('scrollLeft');
useEffect(() => {
const { currentTab, viewport } = props;

const { width } = viewport;
const hasViewport = width > 0;
const hasHorizontalTabLayout = hasViewport && width <= MAX_HORIZONTAL_NAV_SCREEN_WIDTH;
const hasFontsLoaded =
hasViewport && document.documentElement.classList.contains('fontsLoaded');

// Check if scrollToTab call is needed (tab is not visible on mobile)
if (hasHorizontalTabLayout && hasFontsLoaded) {
scrollToTab(currentTab, scrollLeft, setScrollLeft);
}

return () => {
// Update scroll position when unmounting
const el = document.querySelector(`#${currentTab}Tab`);
setScrollLeft(el.parentElement.scrollLeft);
};
});

const { currentTab } = props;

const tabs = [
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { useEffect, useState } from 'react';

// Global state is useful in situations, where UI state needs to be tracked navigation history.
// Alternatively, Redux could be used too, but it's unnecessarily heavy to use for
// a single component to track it's internal UI state (e.g. "scroll position").
// If more than one component is needing certain info, the recommendation is to use Redux.
//
// This piece of code is taken from Daishi Kato's blog:
// https://blog.axlight.com/posts/steps-to-develop-global-state-for-react/
export const createGlobalState = initialState => {
let globalState = initialState;
const listeners = Object.fromEntries(Object.keys(initialState).map(key => [key, new Set()]));

const setGlobalState = (key, nextValue) => {
globalState = { ...globalState, [key]: nextValue };
listeners[key].forEach(listener => listener());
};

const useGlobalState = key => {
const [state, setState] = useState(globalState[key]);
useEffect(() => {
const listener = () => {
setState(globalState[key]);
};
listeners[key].add(listener);
listener(); // in case it's already changed
return () => listeners[key].delete(listener); // cleanup
}, []);
return [state, nextValue => setGlobalState(key, nextValue)];
};

return {
setGlobalState,
useGlobalState,
};
};
7 changes: 3 additions & 4 deletions src/containers/CheckoutPage/CheckoutPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -278,9 +278,8 @@ export class CheckoutPageComponent extends Component {
const { stripe, card, billingDetails, paymentIntent } = handlePaymentParams;
const stripeElementMaybe = selectedPaymentFlow !== USE_SAVED_CARD ? { card } : {};

// Note: payment_method could be set here for USE_SAVED_CARD flow.
// { payment_method: stripePaymentMethodId }
// However, we have set it already on API side, when PaymentIntent was created.
// Note: For basic USE_SAVED_CARD scenario, we have set it already on API side, when PaymentIntent was created.
// However, the payment_method is save here for USE_SAVED_CARD flow if customer first attempted onetime payment
const paymentParams =
selectedPaymentFlow !== USE_SAVED_CARD
? {
Expand All @@ -289,7 +288,7 @@ export class CheckoutPageComponent extends Component {
card: card,
},
}
: {};
: { payment_method: stripePaymentMethodId };

const params = {
stripePaymentIntentClientSecret,
Expand Down
Loading

0 comments on commit ed2c066

Please sign in to comment.