From d2b62302b5d7c9c0c582090e5eba6ee49b10def0 Mon Sep 17 00:00:00 2001 From: Vladimir <70273239+vladimir-8@users.noreply.github.com> Date: Fri, 8 Nov 2024 14:38:35 -0800 Subject: [PATCH 01/14] change: display the next shipping time range instead of the opening hours --- src/components/RestaurantCard.js | 4 +- src/i18n/locales/en.json | 1 + src/navigation/checkout/Restaurant.js | 2 +- .../checkout/components/CartFooterButton.js | 4 +- .../checkout/components/RestaurantBadges.js | 6 +- .../checkout/components/RestaurantProfile.js | 69 ++++++++++--------- src/utils/checkout.js | 61 ++++++++-------- 7 files changed, 77 insertions(+), 70 deletions(-) diff --git a/src/components/RestaurantCard.js b/src/components/RestaurantCard.js index 24156af11..da3555823 100644 --- a/src/components/RestaurantCard.js +++ b/src/components/RestaurantCard.js @@ -3,7 +3,7 @@ import { Text } from 'native-base'; import { Image, StyleSheet, View } from 'react-native'; import { TimingBadge } from '../navigation/checkout/components/RestaurantBadges'; import { useBackgroundContainerColor } from '../styles/theme'; -import { isRestaurantAvailable } from '../utils/checkout'; +import { isRestaurantOrderingAvailable } from '../utils/checkout'; import { RestaurantBadge } from './RestaurantBadge'; import { RestaurantTag } from './RestaurantTag'; import { RestaurantBanner } from './RestaurantBanner'; @@ -92,7 +92,7 @@ const styles = StyleSheet.create({ export const RestaurantCard = ({ restaurant }) => { const backgroundColor = useBackgroundContainerColor(); - const isAvailable = isRestaurantAvailable(restaurant); + const isAvailable = isRestaurantOrderingAvailable(restaurant); return ( diff --git a/src/i18n/locales/en.json b/src/i18n/locales/en.json index 1358bf509..0bb862a20 100644 --- a/src/i18n/locales/en.json +++ b/src/i18n/locales/en.json @@ -425,6 +425,7 @@ "RESTAURANT_PRODUCT_OPTIONS": "Product options", "RESTAURANT_PRE_ORDER": "Pre-order", "RESTAURANT_CLOSED_BUT_OPENS": "This restaurant is closed now but opens {{ datetime }}. You can still order for later on though!", + "RESTAURANT_CLOSED_PRE_ORDER": "This restaurant is closed now. You can still order for {{ datetime }} though!", "RESTAURANT_CLOSED_AND_NOT_AVAILABLE": "This restaurant is closed and not available until {{ datetime }}.", "CHECKOUT_AS_GUEST": "Checkout as guest", "GUEST_CHECKOUT_ORDER_EMAIL_HELP": "To send you updates about your order", diff --git a/src/navigation/checkout/Restaurant.js b/src/navigation/checkout/Restaurant.js index 584e81cb1..061f1f02f 100644 --- a/src/navigation/checkout/Restaurant.js +++ b/src/navigation/checkout/Restaurant.js @@ -221,7 +221,7 @@ function Restaurant(props) { {i18n.t('RESTAURANT_OPENING_HOURS')} { export const TimingBadge = ({ restaurant }) => { const color = useBaseTextColor(); - const isAvailable = isRestaurantAvailable(restaurant); - const shippingTime = getNextShippingTimeAsText(restaurant); + const isAvailable = isRestaurantOrderingAvailable(restaurant); const showPreOrder = shouldShowPreOrder(restaurant); + const shippingTime = getNextShippingTimeAsText(restaurant); return ( diff --git a/src/navigation/checkout/components/RestaurantProfile.js b/src/navigation/checkout/components/RestaurantProfile.js index 63fccc1c2..c8b1e6c16 100644 --- a/src/navigation/checkout/components/RestaurantProfile.js +++ b/src/navigation/checkout/components/RestaurantProfile.js @@ -15,8 +15,8 @@ import DangerAlert from '../../../components/DangerAlert'; import i18n from '../../../i18n'; import moment from 'moment/moment'; import { - isRestaurantAvailable, - isRestaurantOpeningSoon, + getNextShippingTime, + isRestaurantOrderingAvailable, shouldShowPreOrder, } from '../../../utils/checkout'; import { RestaurantBanner } from '../../../components/RestaurantBanner'; @@ -96,17 +96,26 @@ const styles = StyleSheet.create({ }); function OpeningHoursWarning({ - currentTimeSlot, - isAvailable, - isOpeningSoon, + openingHoursSpecification, + isOrderingAvailable, showPreOrder, + nextShippingTime, }) { - if (showPreOrder) { + if (!isOrderingAvailable) { + //FIXME: this is based on the regular opening hours + // and does not take closing rules ("Holidays") into account + const nextOpeningHours = openingHoursSpecification.currentTimeSlot.timeSlot; + + if (!nextOpeningHours) { + // when restaurant is disabled it will be shown on the banner + // and in the 'timing' section + return null; + } + return ( ; } @@ -152,28 +161,26 @@ function RestaurantProfile({ restaurant, openingHoursSpecification, onInfo }) { const stroke = useBaseTextColor(); const textSecondary = useSecondaryTextColor(); - const currentTimeSlot = useMemo( - () => openingHoursSpecification.currentTimeSlot, - [openingHoursSpecification], - ); - - const isAvailable = useMemo( - () => isRestaurantAvailable(restaurant), - [restaurant], - ); - const isOpeningSoon = useMemo( - () => isRestaurantOpeningSoon(restaurant), + const isOrderingAvailable = useMemo( + () => isRestaurantOrderingAvailable(restaurant), [restaurant], ); const showPreOrder = useMemo( () => shouldShowPreOrder(restaurant), [restaurant], ); + const nextShippingTime = useMemo( + () => getNextShippingTime(restaurant), + [restaurant], + ); return ( - + @@ -200,10 +207,10 @@ function RestaurantProfile({ restaurant, openingHoursSpecification, onInfo }) { diff --git a/src/utils/checkout.js b/src/utils/checkout.js index 3e80f8772..bfd650a93 100644 --- a/src/utils/checkout.js +++ b/src/utils/checkout.js @@ -11,13 +11,6 @@ function round5(x) { } function timingAsText(timing, now) { - // FIXME - // This hotfixes a bug on the API - // https://github.com/coopcycle/coopcycle-web/issues/2213 - if (timing.range[0] === timing.range[1]) { - return i18n.t('NOT_AVAILABLE_ATM'); - } - const lower = moment.parseZone(timing.range[0]); if (timing.fast) { @@ -43,18 +36,38 @@ function timingAsText(timing, now) { return lower.calendar(now); } +export function getNextShippingTime(restaurant) { + + const timing = restaurant.timing.delivery || restaurant.timing.collection; + + if (!timing) { + return null; + } + + // FIXME + // This hotfixes a bug on the API + // https://github.com/coopcycle/coopcycle-web/issues/2213 + if (!Array.isArray(timing.range)) { + return null; + } + if (timing.range[0] === timing.range[1]) { + return null; + } + + return timing; +} + + export function getNextShippingTimeAsText(restaurant, now) { now = now || moment(); - if (restaurant.timing.delivery) { - return timingAsText(restaurant.timing.delivery, now); - } + const timing = getNextShippingTime(restaurant); - if (restaurant.timing.collection) { - return timingAsText(restaurant.timing.collection, now); + if (!timing) { + return i18n.t('NOT_AVAILABLE_ATM'); } - return i18n.t('NOT_AVAILABLE_ATM'); + return timingAsText(timing, now); } export function getRestaurantCaption(restaurant) { @@ -65,23 +78,9 @@ export function getRestaurantCaption(restaurant) { * While the restaurant might be available (for ordering) * it might be either opened or closed at the moment */ -export function isRestaurantAvailable(restaurant) { - if (!restaurant.timing.delivery && !restaurant.timing.collection) { - return false; - } - - // FIXME - // This hotfixes a bug on the API - // https://github.com/coopcycle/coopcycle-web/issues/2213 - if ( - restaurant.timing.delivery && - Array.isArray(restaurant.timing.delivery.range) && - restaurant.timing.delivery.range[0] === restaurant.timing.delivery.range[1] - ) { - return false; - } - - return true; +export function isRestaurantOrderingAvailable(restaurant) { + const timing = getNextShippingTime(restaurant); + return Boolean(timing); } /** @@ -105,7 +104,7 @@ export function isRestaurantOpeningSoon(restaurant) { * If the pre-order is soon, we show a regular order button */ export function shouldShowPreOrder(restaurant) { - if (!isRestaurantAvailable(restaurant)) { + if (!isRestaurantOrderingAvailable(restaurant)) { return false; } From 35303c6bb18484cb5ee313f7b79805ba10712f7b Mon Sep 17 00:00:00 2001 From: Vladimir <70273239+vladimir-8@users.noreply.github.com> Date: Tue, 12 Nov 2024 16:29:15 -0800 Subject: [PATCH 02/14] add: translations --- src/i18n/locales/es.json | 1 + src/i18n/locales/fr.json | 1 + 2 files changed, 2 insertions(+) diff --git a/src/i18n/locales/es.json b/src/i18n/locales/es.json index d60259d02..f85f51093 100644 --- a/src/i18n/locales/es.json +++ b/src/i18n/locales/es.json @@ -405,6 +405,7 @@ "PRIVACY": "Privacidad", "RESTAURANT_PRE_ORDER": "Reservar", "RESTAURANT_CLOSED_BUT_OPENS": "Este comercio se encuentra cerrado pero vuelve a abrir {{ datetime }}. Puedes programar tu pedido!", + "RESTAURANT_CLOSED_PRE_ORDER": "Este restaurante está cerrado ahora. ¡Aún puedes pedir para {{ datetime }}!", "RESTAURANT_CLOSED_AND_NOT_AVAILABLE": "Este comercio se encuentra cerrado y no disponible hasta el {{ datetime }}.", "MY_ORDER": "Mi pedido", "CHECKOUT_AS_GUEST": "Comprar como inivitado", diff --git a/src/i18n/locales/fr.json b/src/i18n/locales/fr.json index 8844303a4..294ea7399 100644 --- a/src/i18n/locales/fr.json +++ b/src/i18n/locales/fr.json @@ -401,6 +401,7 @@ "CONNECT_WITH_FACEBOOK": "Se connecter avec Facebook", "RESTAURANT_PRE_ORDER": "Pré-commander", "RESTAURANT_CLOSED_BUT_OPENS": "Ce restaurant est fermé maintenant mais ouvre {{ datetime }}. Vous pouvez quand même commander pour plus tard !", + "RESTAURANT_CLOSED_PRE_ORDER": "Ce restaurant est fermé actuellement, vous pouvez tout même commander pour une livraison à {{ datetime }} !", "RESTAURANT_CLOSED_AND_NOT_AVAILABLE": "Ce restaurant est fermé et non disponible jusqu'à {{ datetime }}.", "CHECKOUT_AS_GUEST": "Commander en tant qu'invité", "GUEST_CHECKOUT_ORDER_EMAIL_HELP": "Pour vous envoyer des mises à jour à propos de votre commande", From 5f371d32b1d555c026d125583f9d24d8851113f5 Mon Sep 17 00:00:00 2001 From: Vladimir <70273239+vladimir-8@users.noreply.github.com> Date: Wed, 20 Nov 2024 18:01:39 -0800 Subject: [PATCH 03/14] added: track current app state --- src/redux/App/actions.js | 2 ++ src/redux/App/reducers.js | 9 ++++++++- .../middlewares/AppStateMiddleware/index.js | 20 +++++++++++++++++++ src/redux/store.js | 2 ++ 4 files changed, 32 insertions(+), 1 deletion(-) create mode 100644 src/redux/middlewares/AppStateMiddleware/index.js diff --git a/src/redux/App/actions.js b/src/redux/App/actions.js index b515c772b..f8fa659bc 100644 --- a/src/redux/App/actions.js +++ b/src/redux/App/actions.js @@ -174,6 +174,8 @@ const loadMyRestaurantsFailure = createFsAction(LOAD_MY_RESTAURANTS_FAILURE); const setSettings = createFsAction(SET_SETTINGS); +export const appStateChanged = createAction('@app/APP_STATE_CHANGED'); + export const setInternetReachable = createFsAction(SET_INTERNET_REACHABLE); export const setBackgroundGeolocationEnabled = createFsAction( diff --git a/src/redux/App/reducers.js b/src/redux/App/reducers.js index dcf3509c1..1e8699e77 100644 --- a/src/redux/App/reducers.js +++ b/src/redux/App/reducers.js @@ -2,7 +2,7 @@ /* * App reducer, dealing with non-domain specific state */ - +import { AppState } from 'react-native'; import Config from 'react-native-config'; import { @@ -54,6 +54,7 @@ import { SET_SERVERS, SET_SETTINGS, SET_USER, + appStateChanged, setSpinnerDelayEnabled, } from './actions'; @@ -68,6 +69,7 @@ const initialState = { httpClient: null, user: null, currentRoute: null, + appState: AppState.currentState, pushNotificationToken: null, pushNotificationTokenSaved: null, loading: false, @@ -449,6 +451,11 @@ export default (state = initialState, action = {}) => { isSpinnerDelayEnabled: action.payload, }; + case appStateChanged.type: + return { + ...state, + appState: action.payload, + }; } return state; diff --git a/src/redux/middlewares/AppStateMiddleware/index.js b/src/redux/middlewares/AppStateMiddleware/index.js new file mode 100644 index 000000000..9d6d2d553 --- /dev/null +++ b/src/redux/middlewares/AppStateMiddleware/index.js @@ -0,0 +1,20 @@ +import { AppState } from 'react-native'; +import { appStateChanged } from '../../App/actions'; + +let initialized = false; +let appState = AppState.currentState; + +export default ({ getState, dispatch }) => { + return next => action => { + if (!initialized) { + AppState.addEventListener('change', nextAppState => { + if (appState !== nextAppState) { + appState = nextAppState; + dispatch(appStateChanged(nextAppState)); + } + }); + initialized = true; + } + return next(action); + }; +}; diff --git a/src/redux/store.js b/src/redux/store.js index 9e8094634..03280342c 100644 --- a/src/redux/store.js +++ b/src/redux/store.js @@ -21,11 +21,13 @@ import { notifyOnNewOrderCreated } from './Restaurant/middlewares'; import { apiSlice } from './api/slice'; import { setupListeners } from '@reduxjs/toolkit/query'; import { setupListenersReactNative } from './setupListenersReactNative'; +import AppStateMiddleware from './middlewares/AppStateMiddleware'; const middlewares = [ thunk, ReduxAsyncQueue, NetInfoMiddleware, + AppStateMiddleware, HttpMiddleware, apiSlice.middleware, PushNotificationMiddleware, From c8770bb874961716a0ff0383a6b6ef2ae2c3c568 Mon Sep 17 00:00:00 2001 From: Vladimir <70273239+vladimir-8@users.noreply.github.com> Date: Wed, 20 Nov 2024 18:10:32 -0800 Subject: [PATCH 04/14] change(Restaurant app): connect to centrifugo based on the app state (active, background etc.) --- src/navigation/restaurant/Dashboard.js | 9 +- src/redux/App/reducers.js | 10 +- .../CentrifugoMiddleware/actions.js | 13 +- .../middlewares/CentrifugoMiddleware/index.js | 169 ++++++++++++------ 4 files changed, 121 insertions(+), 80 deletions(-) diff --git a/src/navigation/restaurant/Dashboard.js b/src/navigation/restaurant/Dashboard.js index b2891e250..81e8fd97d 100644 --- a/src/navigation/restaurant/Dashboard.js +++ b/src/navigation/restaurant/Dashboard.js @@ -31,8 +31,7 @@ import { } from '../../redux/Restaurant/selectors'; import PushNotification from '../../notifications'; import OrdersToPrintQueue from './components/OrdersToPrintQueue'; -import { connect as connectCentrifugo } from '../../redux/middlewares/CentrifugoMiddleware/actions'; -import BasicSafeAreaView from '../../components/BasicSafeAreaView' +import BasicSafeAreaView from '../../components/BasicSafeAreaView'; const RNSound = NativeModules.RNSound; @@ -65,12 +64,6 @@ export default function DashboardPage({ navigation, route }) { }; }, []); - useEffect(() => { - if (!isCentrifugoConnected) { - dispatch(connectCentrifugo()); - } - }, [dispatch, isCentrifugoConnected]); - useEffect(() => { if (route.params?.loadOrders ?? true) { dispatch( diff --git a/src/redux/App/reducers.js b/src/redux/App/reducers.js index 1e8699e77..62f572afd 100644 --- a/src/redux/App/reducers.js +++ b/src/redux/App/reducers.js @@ -5,11 +5,7 @@ import { AppState } from 'react-native'; import Config from 'react-native-config'; -import { - CENTRIFUGO_MESSAGE, - CONNECTED, - DISCONNECTED, -} from '../middlewares/CentrifugoMiddleware'; +import { connected, disconnected } from '../middlewares/CentrifugoMiddleware'; import { ACCEPT_PRIVACY_POLICY, @@ -172,13 +168,13 @@ export default (state = initialState, action = {}) => { loading: action.payload, }; - case CONNECTED: + case connected.type: return { ...state, isCentrifugoConnected: true, }; - case DISCONNECTED: + case disconnected.type: return { ...state, isCentrifugoConnected: false, diff --git a/src/redux/middlewares/CentrifugoMiddleware/actions.js b/src/redux/middlewares/CentrifugoMiddleware/actions.js index e19bb5e2a..f51ce861f 100644 --- a/src/redux/middlewares/CentrifugoMiddleware/actions.js +++ b/src/redux/middlewares/CentrifugoMiddleware/actions.js @@ -1,16 +1,13 @@ -import { createAction } from 'redux-actions'; - +import { createAction } from '@reduxjs/toolkit'; import { updateTask } from '../../Dispatch/actions'; -export const CONNECT = '@centrifugo/CONNECT'; export const CENTRIFUGO_MESSAGE = '@centrifugo/MESSAGE'; -export const CONNECTED = '@centrifugo/CONNECTED'; -export const DISCONNECTED = '@centrifugo/DISCONNECTED'; -export const connect = createAction(CONNECT); +export const connect = createAction('@centrifugo/CONNECT'); +export const disconnect = createAction('@centrifugo/DISCONNECT'); -export const connected = createAction(CONNECTED); -export const disconnected = createAction(DISCONNECTED); +export const connected = createAction('@centrifugo/CONNECTED'); +export const disconnected = createAction('@centrifugo/DISCONNECTED'); export const _message = createAction(CENTRIFUGO_MESSAGE); diff --git a/src/redux/middlewares/CentrifugoMiddleware/index.js b/src/redux/middlewares/CentrifugoMiddleware/index.js index f02cd3a23..c631aeb91 100644 --- a/src/redux/middlewares/CentrifugoMiddleware/index.js +++ b/src/redux/middlewares/CentrifugoMiddleware/index.js @@ -3,10 +3,9 @@ import parseUrl from 'url-parse'; import { CENTRIFUGO_MESSAGE, - CONNECT, - CONNECTED, - DISCONNECTED, + connect, connected, + disconnect, disconnected, message, } from './actions'; @@ -16,81 +15,137 @@ import { selectHttpClient, selectHttpClientHasCredentials, selectIsAuthenticated, + selectLoggedInUser, selectUser, } from '../../App/selectors'; -import { LOGOUT_SUCCESS } from '../../App/actions'; +import { LOGOUT_SUCCESS, appStateChanged } from '../../App/actions'; -const isCentrifugoAction = ({ type }) => [CONNECT].some(x => x === type); +function shouldManageConnectionBasedOnAppState(getState, action) { + const state = getState(); -export default ({ getState, dispatch }) => { - let centrifuge = null; + const user = selectLoggedInUser(state); + + if (!user) { + return false; + } + + // For now this method is used only for restaurant users + return user.hasRole('ROLE_RESTAURANT'); +} + +function shouldConnectBasedOnAppState(getState, action) { + if (!shouldManageConnectionBasedOnAppState(getState, action)) { + return false; + } + + if (action.type !== appStateChanged.type) { + return false; + } + + return action.payload === 'active'; +} + +function shouldDisconnectBasedOnAppState(getState, action) { + if (!shouldManageConnectionBasedOnAppState(getState, action)) { + return false; + } + + if (action.type !== appStateChanged.type) { + return false; + } + + return action.payload !== 'active'; +} + +let centrifuge = null; +let subscription = null; +export default ({ getState, dispatch }) => { return next => action => { if (action.type === LOGOUT_SUCCESS) { - const result = next(action); + dispatch(disconnect()); + return next(action); + } + + if ( + action.type === connect.type || + shouldConnectBasedOnAppState(getState, action) + ) { + const state = getState(); + + if ( + !selectIsAuthenticated(state) || + !selectHttpClientHasCredentials(state) + ) { + return next(action); + } if (centrifuge && centrifuge.isConnected()) { - centrifuge.disconnect(); - centrifuge = null; + return next(action); } - return result; - } + const httpClient = selectHttpClient(state); + const baseURL = selectBaseURL(state); + const user = selectUser(state); + + httpClient.get('/api/centrifugo/token').then(tokenResponse => { + const url = parseUrl(baseURL); + const protocol = url.protocol === 'https:' ? 'wss' : 'ws'; + + centrifuge = new Centrifuge( + `${protocol}://${url.hostname}/centrifugo/connection/websocket`, + { + debug: __DEV__, + onRefresh: function (ctx, cb) { + httpClient + .post('/api/centrifugo/token/refresh') + .then(refreshResponse => { + // @see https://github.com/centrifugal/centrifuge-js#refreshendpoint + // Data must be like {"status": 200, "data": {"token": "JWT"}} - see + // type definitions in dist folder. Note that setting status to 200 is + // required at moment. Any other status will result in refresh process + // failure so client will eventually be disconnected by server. + cb({ status: 200, data: { token: refreshResponse.token } }); + }); + }, + }, + ); + + centrifuge.setToken(tokenResponse.token); + + centrifuge.on('connect', context => dispatch(connected(context))); + centrifuge.on('disconnect', context => dispatch(disconnected(context))); + + subscription = centrifuge.subscribe( + `${tokenResponse.namespace}_events#${user.username}`, + msg => dispatch(message(msg.data.event)), + ); + + centrifuge.connect(); + }); - if (!isCentrifugoAction(action)) { return next(action); } - const state = getState(); - if ( - !selectIsAuthenticated(state) || - !selectHttpClientHasCredentials(state) + action.type === disconnect.type || + shouldDisconnectBasedOnAppState(getState, action) ) { + if (subscription) { + subscription.unsubscribe(); + subscription.removeAllListeners(); + subscription = null; + } + + if (centrifuge && centrifuge.isConnected()) { + centrifuge.disconnect(); + centrifuge = null; + } return next(action); } - const httpClient = selectHttpClient(state); - const baseURL = selectBaseURL(state); - const user = selectUser(state); - - httpClient.get('/api/centrifugo/token').then(tokenResponse => { - const url = parseUrl(baseURL); - const protocol = url.protocol === 'https:' ? 'wss' : 'ws'; - - centrifuge = new Centrifuge( - `${protocol}://${url.hostname}/centrifugo/connection/websocket`, - { - debug: __DEV__, - onRefresh: function (ctx, cb) { - httpClient - .post('/api/centrifugo/token/refresh') - .then(refreshResponse => { - // @see https://github.com/centrifugal/centrifuge-js#refreshendpoint - // Data must be like {"status": 200, "data": {"token": "JWT"}} - see - // type definitions in dist folder. Note that setting status to 200 is - // required at moment. Any other status will result in refresh process - // failure so client will eventually be disconnected by server. - cb({ status: 200, data: { token: refreshResponse.token } }); - }); - }, - }, - ); - centrifuge.setToken(tokenResponse.token); - - centrifuge.on('connect', context => dispatch(connected(context))); - centrifuge.on('disconnect', context => dispatch(disconnected(context))); - - centrifuge.subscribe( - `${tokenResponse.namespace}_events#${user.username}`, - msg => dispatch(message(msg.data.event)), - ); - - centrifuge.connect(); - }); - return next(action); }; }; -export { CENTRIFUGO_MESSAGE, CONNECTED, DISCONNECTED, connected, disconnected }; +export { CENTRIFUGO_MESSAGE, connected, disconnected }; From d6ad3a8c5ad12a0292605cb7ad5689cdbf14bb18 Mon Sep 17 00:00:00 2001 From: Vladimir <70273239+vladimir-8@users.noreply.github.com> Date: Thu, 21 Nov 2024 15:31:16 -0800 Subject: [PATCH 05/14] added: a button to manually re-connect to centrifugo --- src/i18n/locales/en.json | 1 + src/i18n/locales/es.json | 1 + src/i18n/locales/fr.json | 1 + src/navigation/restaurant/Dashboard.js | 3 +- .../components/WebSocketIndicator.js | 60 ++++++++++++++----- src/redux/App/reducers.js | 15 +++++ src/redux/App/selectors.js | 3 + 7 files changed, 67 insertions(+), 17 deletions(-) diff --git a/src/i18n/locales/en.json b/src/i18n/locales/en.json index 2b0b462b4..a0d0d9c9e 100644 --- a/src/i18n/locales/en.json +++ b/src/i18n/locales/en.json @@ -70,6 +70,7 @@ "CALCULATING_YOUR_POS": "Determining your position", "TRACKING_YOUR_POS": "Tracking your position", "CONN_LOST": "Connection lost! Reconnecting", + "CONN_LOST_IDLE": "Connection lost!", "AUTHENTICATING": "Authenticating", "REFUSE": "Refuse", "ACCEPT": "Accept", diff --git a/src/i18n/locales/es.json b/src/i18n/locales/es.json index 882b887f9..086fda245 100644 --- a/src/i18n/locales/es.json +++ b/src/i18n/locales/es.json @@ -65,6 +65,7 @@ "CALCULATING_YOUR_POS": "Cálculando tu posición", "TRACKING_YOUR_POS": "Seguimiento de su posición", "CONN_LOST": "Conexión perdida, reconexión", + "CONN_LOST_IDLE": "Conexión perdida", "AUTHENTICATING": "Autenticando", "REFUSE": "Rechazar", "ACCEPT": "Acceptar", diff --git a/src/i18n/locales/fr.json b/src/i18n/locales/fr.json index cb2a65213..baf60acc3 100644 --- a/src/i18n/locales/fr.json +++ b/src/i18n/locales/fr.json @@ -67,6 +67,7 @@ "CALCULATING_YOUR_POS": "Calcul de votre position", "TRACKING_YOUR_POS": "Suivi de votre position", "CONN_LOST": "Connexion perdue, reconnexion", + "CONN_LOST_IDLE": "Connexion perdue", "AUTHENTICATING": "Authentification…", "REFUSE": "Refuser", "ACCEPT": "Accepter", diff --git a/src/navigation/restaurant/Dashboard.js b/src/navigation/restaurant/Dashboard.js index 81e8fd97d..014020351 100644 --- a/src/navigation/restaurant/Dashboard.js +++ b/src/navigation/restaurant/Dashboard.js @@ -46,7 +46,6 @@ export default function DashboardPage({ navigation, route }) { state => state.app.isInternetReachable, ); const isLoading = useSelector(selectIsLoading); - const isCentrifugoConnected = useSelector(selectIsCentrifugoConnected); const { navigate } = navigation; @@ -171,7 +170,7 @@ export default function DashboardPage({ navigation, route }) { } /> )} - + ( - - - {connected ? t('WAITING_FOR_ORDER') : t('CONN_LOST')} - - - -); +const WebSocketIndicator = () => { + const connecting = useSelector(selectIsCentrifugoConnecting); + const connected = useSelector(selectIsCentrifugoConnected); + + const dispatch = useDispatch(); + + const { t } = useTranslation(); + + return ( + + + {connected + ? t('WAITING_FOR_ORDER') + : connecting + ? t('CONN_LOST') + : t('CONN_LOST_IDLE')} + + {connected ? ( + + ) : null} + {!connected && !connecting ? ( + + ) : null} + + ); +}; const styles = StyleSheet.create({ container: { @@ -41,4 +71,4 @@ const styles = StyleSheet.create({ }, }); -export default withTranslation()(WebSocketIndicator); +export default WebSocketIndicator; diff --git a/src/redux/App/reducers.js b/src/redux/App/reducers.js index 62f572afd..5b65b374a 100644 --- a/src/redux/App/reducers.js +++ b/src/redux/App/reducers.js @@ -56,6 +56,7 @@ import { import { EVENT as EVENT_ORDER } from '../../domain/Order'; import { EVENT as EVENT_TASK_COLLECTION } from '../../domain/TaskCollection'; +import { connect } from '../middlewares/CentrifugoMiddleware/actions'; const initialState = { customBuild: !!Config.DEFAULT_SERVER, @@ -93,6 +94,7 @@ const initialState = { loginByEmailErrors: {}, isBackgroundGeolocationEnabled: false, hasDisclosedBackgroundPermission: false, + isCentrifugoConnecting: false, isCentrifugoConnected: false, modal: { show: false, @@ -168,15 +170,28 @@ export default (state = initialState, action = {}) => { loading: action.payload, }; + case connect.type: { + if (state.isCentrifugoConnected) { + return state; + } + + return { + ...state, + isCentrifugoConnecting: true, + }; + } + case connected.type: return { ...state, + isCentrifugoConnecting: false, isCentrifugoConnected: true, }; case disconnected.type: return { ...state, + isCentrifugoConnecting: false, isCentrifugoConnected: false, }; diff --git a/src/redux/App/selectors.js b/src/redux/App/selectors.js index 70b5c02d8..1e747493a 100644 --- a/src/redux/App/selectors.js +++ b/src/redux/App/selectors.js @@ -72,6 +72,9 @@ export const selectIsLoading = createSelector( }, ); +export const selectIsCentrifugoConnecting = state => + state.app.isCentrifugoConnecting; + export const selectIsCentrifugoConnected = state => state.app.isCentrifugoConnected; From fedc12ff18dfae01f325eee887c7a93e8972bf56 Mon Sep 17 00:00:00 2001 From: Vladimir <70273239+vladimir-8@users.noreply.github.com> Date: Thu, 21 Nov 2024 15:36:23 -0800 Subject: [PATCH 06/14] refactored centrifugo event names --- src/navigation/courier/TasksPage.js | 2 +- .../components/WebSocketIndicator.js | 4 ++-- src/redux/App/__tests__/reducers.test.js | 12 +++++------ src/redux/App/reducers.js | 10 +++++----- src/redux/Dispatch/actions.js | 4 ++-- .../CentrifugoMiddleware/actions.js | 8 ++++---- .../middlewares/CentrifugoMiddleware/index.js | 20 +++++++++---------- 7 files changed, 30 insertions(+), 30 deletions(-) diff --git a/src/navigation/courier/TasksPage.js b/src/navigation/courier/TasksPage.js index 375dc9340..87fa70530 100644 --- a/src/navigation/courier/TasksPage.js +++ b/src/navigation/courier/TasksPage.js @@ -24,7 +24,7 @@ import { selectIsCentrifugoConnected, selectSettingsLatLng, } from '../../redux/App/selectors'; -import { connect as connectCentrifugo } from '../../redux/middlewares/CentrifugoMiddleware/actions'; +import { connectCentrifugo } from '../../redux/middlewares/CentrifugoMiddleware/actions'; import { useGetMyTasksQuery } from '../../redux/api/slice'; const styles = StyleSheet.create({ diff --git a/src/navigation/restaurant/components/WebSocketIndicator.js b/src/navigation/restaurant/components/WebSocketIndicator.js index f20da7402..86aac1322 100644 --- a/src/navigation/restaurant/components/WebSocketIndicator.js +++ b/src/navigation/restaurant/components/WebSocketIndicator.js @@ -7,7 +7,7 @@ import { selectIsCentrifugoConnected, selectIsCentrifugoConnecting, } from '../../../redux/App/selectors'; -import { connect } from '../../../redux/middlewares/CentrifugoMiddleware/actions'; +import { connectCentrifugo } from '../../../redux/middlewares/CentrifugoMiddleware/actions'; const WebSocketIndicator = () => { const connecting = useSelector(selectIsCentrifugoConnecting); @@ -37,7 +37,7 @@ const WebSocketIndicator = () => { diff --git a/src/redux/App/__tests__/reducers.test.js b/src/redux/App/__tests__/reducers.test.js index a4e65d2c5..900cda30b 100644 --- a/src/redux/App/__tests__/reducers.test.js +++ b/src/redux/App/__tests__/reducers.test.js @@ -1,14 +1,14 @@ import { omit } from 'lodash'; import { - connected, - disconnected, + centrifugoConnected, + centrifugoDisconnected, } from '../../middlewares/CentrifugoMiddleware'; import reducer from '../reducers'; describe('Redux | App | Reducers', () => { - test(`${connected}`, () => { + test(`${centrifugoConnected}`, () => { const initialState = reducer(undefined, {}); - const newState = reducer(initialState, connected()); + const newState = reducer(initialState, centrifugoConnected()); const restOldState = omit(initialState, ['isCentrifugoConnected']); const restNewState = omit(newState, ['isCentrifugoConnected']); @@ -21,12 +21,12 @@ describe('Redux | App | Reducers', () => { expect(restNewState).toEqual(restOldState); }); - test(`${disconnected}`, () => { + test(`${centrifugoDisconnected}`, () => { const initialState = { ...reducer(undefined, {}), isCentrifugoConnected: true, }; - const newState = reducer(initialState, disconnected()); + const newState = reducer(initialState, centrifugoDisconnected()); const restOldState = omit(initialState, ['isCentrifugoConnected']); const restNewState = omit(newState, ['isCentrifugoConnected']); diff --git a/src/redux/App/reducers.js b/src/redux/App/reducers.js index 5b65b374a..0e43e41d8 100644 --- a/src/redux/App/reducers.js +++ b/src/redux/App/reducers.js @@ -5,7 +5,7 @@ import { AppState } from 'react-native'; import Config from 'react-native-config'; -import { connected, disconnected } from '../middlewares/CentrifugoMiddleware'; +import { centrifugoConnected, centrifugoDisconnected } from '../middlewares/CentrifugoMiddleware'; import { ACCEPT_PRIVACY_POLICY, @@ -56,7 +56,7 @@ import { import { EVENT as EVENT_ORDER } from '../../domain/Order'; import { EVENT as EVENT_TASK_COLLECTION } from '../../domain/TaskCollection'; -import { connect } from '../middlewares/CentrifugoMiddleware/actions'; +import { connectCentrifugo } from '../middlewares/CentrifugoMiddleware/actions'; const initialState = { customBuild: !!Config.DEFAULT_SERVER, @@ -170,7 +170,7 @@ export default (state = initialState, action = {}) => { loading: action.payload, }; - case connect.type: { + case connectCentrifugo.type: { if (state.isCentrifugoConnected) { return state; } @@ -181,14 +181,14 @@ export default (state = initialState, action = {}) => { }; } - case connected.type: + case centrifugoConnected.type: return { ...state, isCentrifugoConnecting: false, isCentrifugoConnected: true, }; - case disconnected.type: + case centrifugoDisconnected.type: return { ...state, isCentrifugoConnecting: false, diff --git a/src/redux/Dispatch/actions.js b/src/redux/Dispatch/actions.js index 0f7cb37a6..3ee6f715a 100644 --- a/src/redux/Dispatch/actions.js +++ b/src/redux/Dispatch/actions.js @@ -5,7 +5,7 @@ import { createAction } from 'redux-actions'; import _ from 'lodash'; import NavigationHolder from '../../NavigationHolder'; import i18n from '../../i18n'; -import { connect } from '../middlewares/CentrifugoMiddleware/actions'; +import { connectCentrifugo } from '../middlewares/CentrifugoMiddleware/actions'; import { createTaskListFailure, @@ -191,7 +191,7 @@ export function initialize() { dispatch(loadUsersSuccess(users['hydra:member'])); dispatch(loadUnassignedTasksSuccess(unassignedTasks['hydra:member'])); dispatch(loadTaskListsSuccess(taskLists['hydra:member'])); - dispatch(connect()); + dispatch(connectCentrifugo()); dispatch(_initialize()); }) .catch(e => dispatch(loadUnassignedTasksFailure(e))); diff --git a/src/redux/middlewares/CentrifugoMiddleware/actions.js b/src/redux/middlewares/CentrifugoMiddleware/actions.js index f51ce861f..ca285a8f6 100644 --- a/src/redux/middlewares/CentrifugoMiddleware/actions.js +++ b/src/redux/middlewares/CentrifugoMiddleware/actions.js @@ -3,11 +3,11 @@ import { updateTask } from '../../Dispatch/actions'; export const CENTRIFUGO_MESSAGE = '@centrifugo/MESSAGE'; -export const connect = createAction('@centrifugo/CONNECT'); -export const disconnect = createAction('@centrifugo/DISCONNECT'); +export const connectCentrifugo = createAction('@centrifugo/CONNECT'); +export const disconnectCentrifugo = createAction('@centrifugo/DISCONNECT'); -export const connected = createAction('@centrifugo/CONNECTED'); -export const disconnected = createAction('@centrifugo/DISCONNECTED'); +export const centrifugoConnected = createAction('@centrifugo/CONNECTED'); +export const centrifugoDisconnected = createAction('@centrifugo/DISCONNECTED'); export const _message = createAction(CENTRIFUGO_MESSAGE); diff --git a/src/redux/middlewares/CentrifugoMiddleware/index.js b/src/redux/middlewares/CentrifugoMiddleware/index.js index c631aeb91..7ba494ec3 100644 --- a/src/redux/middlewares/CentrifugoMiddleware/index.js +++ b/src/redux/middlewares/CentrifugoMiddleware/index.js @@ -3,10 +3,10 @@ import parseUrl from 'url-parse'; import { CENTRIFUGO_MESSAGE, - connect, - connected, - disconnect, - disconnected, + connectCentrifugo, + centrifugoConnected, + disconnectCentrifugo, + centrifugoDisconnected, message, } from './actions'; @@ -63,12 +63,12 @@ let subscription = null; export default ({ getState, dispatch }) => { return next => action => { if (action.type === LOGOUT_SUCCESS) { - dispatch(disconnect()); + dispatch(disconnectCentrifugo()); return next(action); } if ( - action.type === connect.type || + action.type === connectCentrifugo.type || shouldConnectBasedOnAppState(getState, action) ) { const state = getState(); @@ -113,8 +113,8 @@ export default ({ getState, dispatch }) => { centrifuge.setToken(tokenResponse.token); - centrifuge.on('connect', context => dispatch(connected(context))); - centrifuge.on('disconnect', context => dispatch(disconnected(context))); + centrifuge.on('connect', context => dispatch(centrifugoConnected(context))); + centrifuge.on('disconnect', context => dispatch(centrifugoDisconnected(context))); subscription = centrifuge.subscribe( `${tokenResponse.namespace}_events#${user.username}`, @@ -128,7 +128,7 @@ export default ({ getState, dispatch }) => { } if ( - action.type === disconnect.type || + action.type === disconnectCentrifugo.type || shouldDisconnectBasedOnAppState(getState, action) ) { if (subscription) { @@ -148,4 +148,4 @@ export default ({ getState, dispatch }) => { }; }; -export { CENTRIFUGO_MESSAGE, connected, disconnected }; +export { CENTRIFUGO_MESSAGE, centrifugoConnected, centrifugoDisconnected }; From d0d238e939dd4b9c1125d08c35ea2390715d0f68 Mon Sep 17 00:00:00 2001 From: Vladimir <70273239+vladimir-8@users.noreply.github.com> Date: Thu, 21 Nov 2024 15:48:29 -0800 Subject: [PATCH 07/14] fixed: AppState mock --- jestSetup.js | 37 ++++++++++--------- .../Restaurant/__tests__/middlewares.test.js | 6 --- 2 files changed, 19 insertions(+), 24 deletions(-) diff --git a/jestSetup.js b/jestSetup.js index 1bfa6fdb6..d0ca7d6db 100644 --- a/jestSetup.js +++ b/jestSetup.js @@ -1,6 +1,10 @@ /* global jest */ -import NavigationHolder from './src/NavigationHolder' +import NavigationHolder from './src/NavigationHolder'; + +jest.mock('react-native/Libraries/AppState/AppState', () => ({ + currentState: 'active', +})); jest.mock('react-native-localize', () => ({ findBestLanguageTag: () => ({ languageTag: 'en' }), @@ -8,26 +12,25 @@ jest.mock('react-native-localize', () => ({ jest.mock('react-native-blob-util', () => ({ fetch: () => {}, - wrap: () => {} + wrap: () => {}, })); jest.mock('expo-file-system', () => ({ createUploadTask: jest.fn(), FileSystemUploadType: { - MULTIPART: 1 + MULTIPART: 1, }, FileSystemSessionType: { - BACKGROUND: 0 - } + BACKGROUND: 0, + }, })); jest.mock('@react-native-firebase/analytics', () => ({ logEvent: jest.fn(), setUserProperty: jest.fn(), -})) +})); -jest.mock('@react-native-firebase/messaging', () => ({ -})) +jest.mock('@react-native-firebase/messaging', () => ({})); jest.mock('countly-sdk-react-native-bridge', () => ({ enableParameterTamperingProtection: () => {}, @@ -46,22 +49,20 @@ jest.mock('react-native-background-geolocation', () => ({ stop: jest.fn(), removeListeners: jest.fn(), changePace: jest.fn(), -})) +})); -jest.mock('@stripe/stripe-react-native', () => ({ -})) +jest.mock('@stripe/stripe-react-native', () => ({})); -jest.mock('react-native-share', () => ({ -})) +jest.mock('react-native-share', () => ({})); jest.mock('uuid', () => ({ v4: jest.fn(), -})) +})); const fakeNavigator = { current: { - dispatch: (action) => {} - } -} + dispatch: action => {}, + }, +}; -NavigationHolder.setNavigationRef(fakeNavigator) +NavigationHolder.setNavigationRef(fakeNavigator); diff --git a/src/redux/Restaurant/__tests__/middlewares.test.js b/src/redux/Restaurant/__tests__/middlewares.test.js index e6d7df7b1..e83ac2ece 100644 --- a/src/redux/Restaurant/__tests__/middlewares.test.js +++ b/src/redux/Restaurant/__tests__/middlewares.test.js @@ -8,12 +8,6 @@ import { notifyOnNewOrderCreated } from '../middlewares'; import restaurantReducer from '../reducers'; describe('notifyOnNewOrderCreated', () => { - beforeEach(() => { - jest.mock('react-native/Libraries/AppState/AppState', () => ({ - currentState: 'active', - })); - }); - it('does nothing with action type "LOAD_ORDERS_SUCCESS"', () => { const preloadedState = { app: { From 11bc848bcbf9011ec34ea63cb6a1c25204e0e64c Mon Sep 17 00:00:00 2001 From: Vladimir <70273239+vladimir-8@users.noreply.github.com> Date: Thu, 21 Nov 2024 16:25:35 -0800 Subject: [PATCH 08/14] fixed: the notifications sound might stop too early --- src/components/NotificationHandler.js | 29 +++++++++++++++++---------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/src/components/NotificationHandler.js b/src/components/NotificationHandler.js index 743870987..98a6bed78 100644 --- a/src/components/NotificationHandler.js +++ b/src/components/NotificationHandler.js @@ -1,4 +1,4 @@ -import React, { useEffect } from 'react'; +import React, { useEffect, useMemo } from 'react'; import { useDispatch, useSelector } from 'react-redux'; import { @@ -26,32 +26,39 @@ export default function NotificationHandler() { const notificationsToDisplay = useSelector(selectNotificationsToDisplay); const notificationsWithSound = useSelector(selectNotificationsWithSound); + const hasNotifications = useMemo( + () => + notificationsToDisplay.length > 0 || notificationsWithSound.length > 0, + [notificationsToDisplay, notificationsWithSound], + ); + + const hasNotificationsWithSound = useMemo( + () => notificationsWithSound.length > 0, + [notificationsWithSound], + ); + const dispatch = useDispatch(); useEffect(() => { - if ( - notificationsToDisplay.length > 0 || - notificationsWithSound.length > 0 - ) { + // use memoized value to avoid re-setting timeout when more notifications arrive + if (hasNotifications) { setTimeout(() => { dispatch(clearNotifications()); }, NOTIFICATION_DURATION_MS); } - }, [notificationsToDisplay, notificationsWithSound, dispatch]); + }, [hasNotifications, dispatch]); useEffect(() => { // on Android, when notification is received, OS let us execute some code // but it's very limited, e.g. handlers set via setTimeout are not executed // so we do not play sound in that case, because we will not be able to stop it - if ( - notificationsWithSound.length > 0 && - AppState.currentState === 'active' - ) { + // use memoized value to avoid re-starting the sound when more notifications arrive + if (hasNotificationsWithSound && AppState.currentState === 'active') { dispatch(startSound()); } else { dispatch(stopSound()); } - }, [notificationsWithSound, dispatch]); + }, [hasNotificationsWithSound, dispatch]); return ( Date: Thu, 21 Nov 2024 16:37:09 -0800 Subject: [PATCH 09/14] removed reduntant import --- .../components/WebSocketIndicator.js | 2 +- src/redux/App/reducers.js | 7 +++++-- .../middlewares/CentrifugoMiddleware/index.js | 20 ++++++++++++++----- 3 files changed, 21 insertions(+), 8 deletions(-) diff --git a/src/navigation/restaurant/components/WebSocketIndicator.js b/src/navigation/restaurant/components/WebSocketIndicator.js index 86aac1322..e1299d960 100644 --- a/src/navigation/restaurant/components/WebSocketIndicator.js +++ b/src/navigation/restaurant/components/WebSocketIndicator.js @@ -7,7 +7,7 @@ import { selectIsCentrifugoConnected, selectIsCentrifugoConnecting, } from '../../../redux/App/selectors'; -import { connectCentrifugo } from '../../../redux/middlewares/CentrifugoMiddleware/actions'; +import { connectCentrifugo } from '../../../redux/middlewares/CentrifugoMiddleware'; const WebSocketIndicator = () => { const connecting = useSelector(selectIsCentrifugoConnecting); diff --git a/src/redux/App/reducers.js b/src/redux/App/reducers.js index 0e43e41d8..fc1a43bd4 100644 --- a/src/redux/App/reducers.js +++ b/src/redux/App/reducers.js @@ -5,7 +5,11 @@ import { AppState } from 'react-native'; import Config from 'react-native-config'; -import { centrifugoConnected, centrifugoDisconnected } from '../middlewares/CentrifugoMiddleware'; +import { + centrifugoConnected, + centrifugoDisconnected, + connectCentrifugo, +} from '../middlewares/CentrifugoMiddleware'; import { ACCEPT_PRIVACY_POLICY, @@ -56,7 +60,6 @@ import { import { EVENT as EVENT_ORDER } from '../../domain/Order'; import { EVENT as EVENT_TASK_COLLECTION } from '../../domain/TaskCollection'; -import { connectCentrifugo } from '../middlewares/CentrifugoMiddleware/actions'; const initialState = { customBuild: !!Config.DEFAULT_SERVER, diff --git a/src/redux/middlewares/CentrifugoMiddleware/index.js b/src/redux/middlewares/CentrifugoMiddleware/index.js index 7ba494ec3..9b85570fe 100644 --- a/src/redux/middlewares/CentrifugoMiddleware/index.js +++ b/src/redux/middlewares/CentrifugoMiddleware/index.js @@ -3,10 +3,10 @@ import parseUrl from 'url-parse'; import { CENTRIFUGO_MESSAGE, - connectCentrifugo, centrifugoConnected, - disconnectCentrifugo, centrifugoDisconnected, + connectCentrifugo, + disconnectCentrifugo, message, } from './actions'; @@ -113,8 +113,12 @@ export default ({ getState, dispatch }) => { centrifuge.setToken(tokenResponse.token); - centrifuge.on('connect', context => dispatch(centrifugoConnected(context))); - centrifuge.on('disconnect', context => dispatch(centrifugoDisconnected(context))); + centrifuge.on('connect', context => + dispatch(centrifugoConnected(context)), + ); + centrifuge.on('disconnect', context => + dispatch(centrifugoDisconnected(context)), + ); subscription = centrifuge.subscribe( `${tokenResponse.namespace}_events#${user.username}`, @@ -148,4 +152,10 @@ export default ({ getState, dispatch }) => { }; }; -export { CENTRIFUGO_MESSAGE, centrifugoConnected, centrifugoDisconnected }; +export { + CENTRIFUGO_MESSAGE, + centrifugoConnected, + centrifugoDisconnected, + connectCentrifugo, + disconnectCentrifugo, +}; From 6630fd191d9209750f83a42c13d02872dcbd0fdd Mon Sep 17 00:00:00 2001 From: Vladimir <70273239+vladimir-8@users.noreply.github.com> Date: Thu, 28 Nov 2024 15:41:48 -0800 Subject: [PATCH 10/14] added: track timing modal --- src/navigation/checkout/components/TimeRangeChangedModal.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/navigation/checkout/components/TimeRangeChangedModal.js b/src/navigation/checkout/components/TimeRangeChangedModal.js index 6e5525e52..d26fc4b23 100644 --- a/src/navigation/checkout/components/TimeRangeChangedModal.js +++ b/src/navigation/checkout/components/TimeRangeChangedModal.js @@ -20,6 +20,7 @@ import { useGetOrderTimingQuery } from '../../../redux/api/slice'; import { useNavigation } from '@react-navigation/native'; import { useIsModalVisible } from '../../../hooks/useIsModalVisible'; import tracker from '../../../analytics/Tracker'; +import { DatadogLogger } from '../../../Datadog' const styles = StyleSheet.create({ modalContent: { @@ -140,6 +141,7 @@ export default function TimeRangeChangedModal() { isVisible={isModalVisible} onModalHide={onModalHide} onModalShow={() => { + DatadogLogger.info('TimeRangeChangedModal opened'); tracker.logEvent('Checkout', 'openTimeRangeChangedModal'); }}> Date: Thu, 28 Nov 2024 15:55:10 -0800 Subject: [PATCH 11/14] bump version code --- android/app/build.gradle | 2 +- ios/CoopCycle/Info.plist | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index 1880caf68..e65af9a13 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -92,7 +92,7 @@ android { multiDexEnabled true // No need to increment the versionCode, it will be automatically incremented by the fastlane versionCode 218 - versionName "2.27.2" + versionName "2.28.0" manifestPlaceholders = [ tipsiStripeRedirectScheme: "coopcycle", diff --git a/ios/CoopCycle/Info.plist b/ios/CoopCycle/Info.plist index 8485c8422..3b94cdb51 100644 --- a/ios/CoopCycle/Info.plist +++ b/ios/CoopCycle/Info.plist @@ -17,7 +17,7 @@ CFBundlePackageType APPL CFBundleShortVersionString - 2.27.1 + 2.28.0 CFBundleSignature ???? CFBundleURLTypes From b77f8b532877a4974e60e0f4c1cfefeadf639af0 Mon Sep 17 00:00:00 2001 From: Vladimir <70273239+vladimir-8@users.noreply.github.com> Date: Thu, 28 Nov 2024 16:55:46 -0800 Subject: [PATCH 12/14] fix: generate a unique build artifact --- .github/workflows/fastlane_ios.yml | 4 ++-- ios/fastlane/Fastfile | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/fastlane_ios.yml b/.github/workflows/fastlane_ios.yml index 2dc038f8f..6208f4cf2 100644 --- a/.github/workflows/fastlane_ios.yml +++ b/.github/workflows/fastlane_ios.yml @@ -134,5 +134,5 @@ jobs: - name: Upload IPA artifact uses: actions/upload-artifact@v4 with: - name: example-iOS.ipa - path: ios/example-iOS.ipa + name: ${{ inputs.app_name }}_release_iOS.ipa + path: ios/release-iOS.ipa diff --git a/ios/fastlane/Fastfile b/ios/fastlane/Fastfile index 97a1f3097..3411f88b4 100644 --- a/ios/fastlane/Fastfile +++ b/ios/fastlane/Fastfile @@ -208,7 +208,7 @@ platform :ios do app_identifier = deliver_config[:app_identifier] profile_name = lane_context[SharedValues::SIGH_NAME] - output_name = "example-iOS" # specify the name of the .ipa file to generate + output_name = "release-iOS" # specify the name of the .ipa file to generate export_method = "app-store" # specify the export method compile_bitcode = true # specify whether to enable bitcode From c951054c59ddcd9932d2b6c230b156318bf85475 Mon Sep 17 00:00:00 2001 From: Vladimir <70273239+vladimir-8@users.noreply.github.com> Date: Thu, 28 Nov 2024 17:37:47 -0800 Subject: [PATCH 13/14] hotfix: keep the default file name --- .github/workflows/fastlane_ios.yml | 2 +- ios/fastlane/Fastfile | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/fastlane_ios.yml b/.github/workflows/fastlane_ios.yml index 6208f4cf2..1f74b365d 100644 --- a/.github/workflows/fastlane_ios.yml +++ b/.github/workflows/fastlane_ios.yml @@ -135,4 +135,4 @@ jobs: uses: actions/upload-artifact@v4 with: name: ${{ inputs.app_name }}_release_iOS.ipa - path: ios/release-iOS.ipa + path: ios/example-iOS.ipa diff --git a/ios/fastlane/Fastfile b/ios/fastlane/Fastfile index 3411f88b4..97a1f3097 100644 --- a/ios/fastlane/Fastfile +++ b/ios/fastlane/Fastfile @@ -208,7 +208,7 @@ platform :ios do app_identifier = deliver_config[:app_identifier] profile_name = lane_context[SharedValues::SIGH_NAME] - output_name = "release-iOS" # specify the name of the .ipa file to generate + output_name = "example-iOS" # specify the name of the .ipa file to generate export_method = "app-store" # specify the export method compile_bitcode = true # specify whether to enable bitcode From dbdba2362e76995711297ebfb038b7f5eb3ae108 Mon Sep 17 00:00:00 2001 From: Vladimir <70273239+vladimir-8@users.noreply.github.com> Date: Tue, 3 Dec 2024 16:48:48 -0800 Subject: [PATCH 14/14] hotfix: disable crash-reporting to datadog --- src/Datadog.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Datadog.js b/src/Datadog.js index 9463f28d9..3833add54 100644 --- a/src/Datadog.js +++ b/src/Datadog.js @@ -29,14 +29,16 @@ const datadogConfig = new DatadogProviderConfiguration( applicationId, true, // track User interactions (e.g.: Tap on buttons. You can use 'accessibilityLabel' element property to give tap action the name, otherwise element type will be reported) true, // track XHR Resources - true, // track Errors + //FIXME: re-enable after migrating to react-native 0.76: https://github.com/facebook/hermes/issues/1496 + false, // track Errors ); // Optional: Select your Datadog website (one of "US1", "EU1", "US3", "US5", "AP1" or "GOV") datadogConfig.site = 'US1'; // Optional: Enable JavaScript long task collection datadogConfig.longTaskThresholdMs = 100; // Optional: enable or disable native crash reports -datadogConfig.nativeCrashReportEnabled = true; +//FIXME: re-enable after migrating to react-native 0.76: https://github.com/facebook/hermes/issues/1496 +datadogConfig.nativeCrashReportEnabled = false; // Optional: Sample RUM sessions (% of session are sent to Datadog. Default is 100%). datadogConfig.sessionSamplingRate = __DEV__ ? 100 : 20; // Optional: Sample tracing integrations for network calls between your app and your backend (% of calls to your instrumented backend are linked from the RUM view to the APM view. Default is 20%)