diff --git a/src/reference-data/utils.ts b/src/reference-data/utils.ts
index 90e114d059..7a62238c4b 100644
--- a/src/reference-data/utils.ts
+++ b/src/reference-data/utils.ts
@@ -34,6 +34,10 @@ export const findReferenceDataById = <
id: string,
) => elements.find((p) => p.id === id);
+export function isOfFareProductRef(a: any): a is {fareProductRef: string} {
+ return 'fareProductRef' in a;
+}
+
export const productIsSellableInApp = (product: PreassignedFareProduct) => {
if (
(product.limitations.appVersionMin &&
diff --git a/src/select-travel-token-screen/SelectTravelTokenScreenComponent.tsx b/src/select-travel-token-screen/SelectTravelTokenScreenComponent.tsx
index 89ebe3b7cd..5eed9f988b 100644
--- a/src/select-travel-token-screen/SelectTravelTokenScreenComponent.tsx
+++ b/src/select-travel-token-screen/SelectTravelTokenScreenComponent.tsx
@@ -18,7 +18,11 @@ import {
isCarnetTravelRight,
useTicketingState,
} from '@atb/ticketing';
-import {TravelTokenTexts, useTranslation} from '@atb/translations';
+import {
+ TravelTokenTexts,
+ getTextForLanguage,
+ useTranslation,
+} from '@atb/translations';
import {animateNextChange} from '@atb/utils/animation';
import {flatMap} from '@atb/utils/array';
import React, {useCallback, useState} from 'react';
@@ -26,15 +30,23 @@ import {ActivityIndicator, View} from 'react-native';
import {ScrollView} from 'react-native-gesture-handler';
import {RadioGroupSection, Section} from '@atb/components/sections';
import {useRemoteConfig} from '@atb/RemoteConfigContext';
+import {useFirestoreConfiguration} from '@atb/configuration';
+import {onlyUniquesBasedOnField} from '@atb/utils/only-uniques';
+import {
+ findReferenceDataById,
+ isOfFareProductRef,
+} from '@atb/reference-data/utils';
type Props = {onAfterSave: () => void};
export const SelectTravelTokenScreenComponent = ({onAfterSave}: Props) => {
const styles = useStyles();
- const {t} = useTranslation();
+ const {t, language} = useTranslation();
const {fareContracts} = useTicketingState();
const {disable_travelcard} = useRemoteConfig();
+ const {fareProductTypeConfigs, preassignedFareProducts} =
+ useFirestoreConfiguration();
const {token, remoteTokens, toggleToken} = useMobileTokenContextState();
const inspectableToken = findInspectable(remoteTokens);
@@ -47,10 +59,36 @@ export const SelectTravelTokenScreenComponent = ({onAfterSave}: Props) => {
inspectableToken,
);
- const hasActiveCarnetFareContract = flatMap(
+ const activeFareContracts = flatMap(
filterActiveOrCanBeUsedFareContracts(fareContracts),
(i) => i.travelRights,
- ).some(isCarnetTravelRight);
+ );
+
+ const hasActiveCarnetFareContract =
+ activeFareContracts.some(isCarnetTravelRight);
+
+ // Filter for unique travel rights config types
+ const activeFareContractsTypes = activeFareContracts
+ .filter(onlyUniquesBasedOnField('type'))
+ .map((travelRight) => {
+ const preassignedFareProduct = findReferenceDataById(
+ preassignedFareProducts,
+ isOfFareProductRef(travelRight) ? travelRight.fareProductRef : '',
+ );
+
+ return (
+ preassignedFareProduct &&
+ fareProductTypeConfigs.find(
+ (c) => c.type === preassignedFareProduct.type,
+ )
+ );
+ });
+
+ const fareProductConfigWhichRequiresTokenOnMobile =
+ activeFareContractsTypes.find(
+ (fareProductTypeConfig) =>
+ fareProductTypeConfig?.configuration.requiresTokenOnMobile === true,
+ );
const [saveState, setSaveState] = useState({
saving: false,
@@ -76,6 +114,14 @@ export const SelectTravelTokenScreenComponent = ({onAfterSave}: Props) => {
const travelCardToken = remoteTokens?.find(isTravelCardToken);
const mobileTokens = remoteTokens?.filter(isMobileToken);
+ // Shows an error message if switching to a t:card,
+ // but the current inspectable token is in the mobile AND
+ // requires mobile token
+ const requiresTokenOnMobile =
+ selectedType === 'travelCard' &&
+ isMobileToken(inspectableToken) &&
+ !!fareProductConfigWhichRequiresTokenOnMobile;
+
return (
{
isMarkdown={false}
/>
)}
+
+ {requiresTokenOnMobile && (
+
+ )}
{selectedType === 'mobile' && mobileTokens?.length ? (
@@ -202,13 +267,15 @@ export const SelectTravelTokenScreenComponent = ({onAfterSave}: Props) => {
{saveState.saving ? (
) : (
-
+ !requiresTokenOnMobile && (
+
+ )
)}
diff --git a/src/stacks-hierarchy/Root_FareContractDetailsScreen.tsx b/src/stacks-hierarchy/Root_FareContractDetailsScreen.tsx
index e57140da1f..9916b31d8f 100644
--- a/src/stacks-hierarchy/Root_FareContractDetailsScreen.tsx
+++ b/src/stacks-hierarchy/Root_FareContractDetailsScreen.tsx
@@ -1,7 +1,10 @@
import {MessageBox} from '@atb/components/message-box';
import {FullScreenHeader} from '@atb/components/screen-header';
import {useFirestoreConfiguration} from '@atb/configuration/FirestoreConfigurationContext';
-import {findReferenceDataById} from '@atb/reference-data/utils';
+import {
+ findReferenceDataById,
+ isOfFareProductRef,
+} from '@atb/reference-data/utils';
import {StyleSheet} from '@atb/theme';
import {isPreActivatedTravelRight, useTicketingState} from '@atb/ticketing';
import {FareContractTexts, useTranslation} from '@atb/translations';
@@ -14,10 +17,6 @@ import {RootStackScreenProps} from '../stacks-hierarchy/navigation-types';
type Props = RootStackScreenProps<'Root_FareContractDetailsScreen'>;
-function isOfFareProductRef(a: any): a is {fareProductRef: string} {
- return 'fareProductRef' in a;
-}
-
export function Root_FareContractDetailsScreen({navigation, route}: Props) {
const styles = useStyles();
const [now, setNow] = useState(Date.now());
diff --git a/src/translations/screens/subscreens/TravelToken.ts b/src/translations/screens/subscreens/TravelToken.ts
index cb8c794d48..c2ae7c8e70 100644
--- a/src/translations/screens/subscreens/TravelToken.ts
+++ b/src/translations/screens/subscreens/TravelToken.ts
@@ -202,6 +202,14 @@ const SelectTravelTokenTexts = {
'Du har et klippekort. Foreløpig fungerer ikke klippekort på mobil. Hvis du bytter til mobil vil du ikke kunne bruke klippekortet ditt.',
`You have an active punch card. Currently punch cards cannot be activated using a phone. If you change to a phone now, you will not be able to use your punch card.`,
),
+ notAllowedToUseCarnetError: {
+ title: _('Kan ikke brukes på t:kort', 'Cannot be used on t:cards'),
+ message: (ticketName: string) =>
+ _(
+ `Du har en aktiv ${ticketName}. Det er derfor ikke mulig å bytte til t:kort.`,
+ `You have an active ${ticketName}. It is therefore not possible to switch to a t:card.`,
+ ),
+ },
unnamedDevice: _('Enhet uten navn', 'Unnamed device'),
},
};