mirror of
https://github.com/trezor/trezor-suite.git
synced 2026-03-21 22:57:17 +01:00
feat(suite-common): add geolocation condition to the message system
This commit is contained in:
committed by
Tomáš Klíma
parent
fa21b2c05b
commit
e8c5a56570
@@ -281,7 +281,8 @@ Structure of config, types and optionality of specific keys can be found in the
|
||||
"duration": {
|
||||
"from": "2021-03-01T12:10:00.000Z",
|
||||
"to": "2022-01-31T12:10:00.000Z"
|
||||
}
|
||||
},
|
||||
"countryCodes": ["CZ", "US"]
|
||||
}
|
||||
],
|
||||
// Detail of an experiment
|
||||
|
||||
@@ -151,6 +151,7 @@ export const getJWSPublicKey = (use: JWSPublicKeyUse) => {
|
||||
|
||||
return isCodesignBuild() ? firmwareConfigPublicKey.codesign : firmwareConfigPublicKey.dev;
|
||||
};
|
||||
|
||||
export const envUtils: EnvUtils = {
|
||||
isWeb,
|
||||
isDesktop,
|
||||
|
||||
@@ -38,3 +38,4 @@ export const REQUEST_DEVICE_RECONNECT = '@suite/request-device-reconnect';
|
||||
export const SET_EXPERIMENTAL_FEATURES = '@suite/set-experimental-features';
|
||||
export const SET_SIDEBAR_WIDTH = '@suite/set-sidebar-width';
|
||||
export const SET_IS_COINS_FILTER_VISIBLE = '@suite/set-is-coins-filter-visible';
|
||||
export const SET_COUNTRY_CODE = '@suite/set-country-code';
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { createAction } from '@reduxjs/toolkit';
|
||||
|
||||
import { notificationsActions } from '@suite-common/toast-notifications';
|
||||
import type { TradingType } from '@suite-common/trading';
|
||||
import type { CountryCode, TradingType } from '@suite-common/trading';
|
||||
import { deviceActions } from '@suite-common/wallet-core';
|
||||
import { getCustomBackends } from '@suite-common/wallet-utils';
|
||||
import { EventType, analytics } from '@trezor/suite-analytics';
|
||||
@@ -62,6 +62,7 @@ export type SuiteAction =
|
||||
symbol: keyof EvmSettings['explanationBannerClosed'];
|
||||
}
|
||||
| { type: typeof SUITE.DISMISSED_TRADING_TERMS; tradingType: TradingType }
|
||||
| { type: typeof SUITE.SET_COUNTRY_CODE; payload: CountryCode }
|
||||
| { type: typeof SUITE.APP_CHANGED; payload: AppState['router']['app'] }
|
||||
| {
|
||||
type: typeof SUITE.SET_THEME;
|
||||
@@ -166,6 +167,11 @@ export const setDismissedTradingTerms = (tradingType: TradingType): SuiteAction
|
||||
tradingType,
|
||||
});
|
||||
|
||||
export const setCountryCode = (countryCode: CountryCode): SuiteAction => ({
|
||||
type: SUITE.SET_COUNTRY_CODE,
|
||||
payload: countryCode,
|
||||
});
|
||||
|
||||
/**
|
||||
* Triggered by `@suite-support/OnlineStatus` or `@suite-native/support/OnlineStatus`
|
||||
* Set `online` status in suite reducer
|
||||
|
||||
@@ -246,6 +246,7 @@ export const getRootReducer = (selectedAccount = BTC_ACCOUNT, fees = DEFAULT_FEE
|
||||
dismissedTradingTerms: {},
|
||||
prefillFields: { sendForm: '', transactionHistory: '' },
|
||||
flags: { stakeEthBannerClosed: false, stakeSolBannerClosed: false },
|
||||
countryCode: null,
|
||||
},
|
||||
() => ({}),
|
||||
),
|
||||
|
||||
@@ -9,7 +9,7 @@ import { changeNetworks, deviceActions, selectSelectedDevice } from '@suite-comm
|
||||
import { DEVICE, TRANSPORT } from '@trezor/connect';
|
||||
|
||||
import { SUITE } from 'src/actions/suite/constants';
|
||||
import { selectActiveTransports } from 'src/reducers/suite/suiteReducer';
|
||||
import { selectActiveTransports, selectCountryCode } from 'src/reducers/suite/suiteReducer';
|
||||
import { getIsTorEnabled } from 'src/utils/suite/tor';
|
||||
|
||||
// actions which can affect message system messages
|
||||
@@ -20,6 +20,7 @@ const actions = [
|
||||
changeNetworks.type,
|
||||
TRANSPORT.START,
|
||||
DEVICE.CONNECT,
|
||||
SUITE.SET_COUNTRY_CODE,
|
||||
];
|
||||
|
||||
const messageSystemMiddleware = createMiddleware(async (action, { next, dispatch, getState }) => {
|
||||
@@ -31,6 +32,7 @@ const messageSystemMiddleware = createMiddleware(async (action, { next, dispatch
|
||||
const transports = selectActiveTransports(getState());
|
||||
const device = selectSelectedDevice(getState());
|
||||
const { enabledNetworks } = getState().wallet.settings;
|
||||
const countryCode = selectCountryCode(getState());
|
||||
|
||||
const validationParams = {
|
||||
device,
|
||||
@@ -39,6 +41,7 @@ const messageSystemMiddleware = createMiddleware(async (action, { next, dispatch
|
||||
tor: getIsTorEnabled(torStatus),
|
||||
enabledNetworks,
|
||||
},
|
||||
countryCode,
|
||||
};
|
||||
|
||||
const [validMessages, validExperimentIds] = await Promise.all([
|
||||
|
||||
@@ -2,12 +2,18 @@ import { MiddlewareAPI } from 'redux';
|
||||
|
||||
import { ROUTER } from 'src/actions/suite/constants';
|
||||
import { Action, AppState, Dispatch } from 'src/types/suite';
|
||||
import { fetchCountryCodeThunk, shouldFetchCountryCode } from 'src/utils/suite/countryCode';
|
||||
|
||||
const router = (api: MiddlewareAPI<Dispatch, AppState>) => (next: Dispatch) => (action: Action) => {
|
||||
const { router } = api.getState();
|
||||
const { router, suite } = api.getState();
|
||||
|
||||
switch (action.type) {
|
||||
case ROUTER.LOCATION_CHANGE:
|
||||
case ROUTER.LOCATION_CHANGE: {
|
||||
// Fetch country code only when entering trading or staking routes
|
||||
if (suite.countryCode == null && shouldFetchCountryCode(action.payload.route?.name)) {
|
||||
api.dispatch(fetchCountryCodeThunk());
|
||||
}
|
||||
|
||||
/**
|
||||
* Store back route for navigation when closing the settings.
|
||||
* Exclude settings routes – we want to close the settings and not just switch the settings tab...
|
||||
@@ -28,6 +34,7 @@ const router = (api: MiddlewareAPI<Dispatch, AppState>) => (next: Dispatch) => (
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ import { produce } from 'immer';
|
||||
|
||||
import { Feature, selectIsFeatureDisabled } from '@suite-common/message-system';
|
||||
import { isDeviceAcquired } from '@suite-common/suite-utils';
|
||||
import type { InvityServerEnvironment, TradingType } from '@suite-common/trading';
|
||||
import type { CountryCode, InvityServerEnvironment, TradingType } from '@suite-common/trading';
|
||||
import { NetworkSymbol } from '@suite-common/wallet-config';
|
||||
import {
|
||||
DeviceRootState,
|
||||
@@ -137,6 +137,7 @@ export interface SuiteState {
|
||||
flags: Flags;
|
||||
evmSettings: EvmSettings;
|
||||
dismissedTradingTerms: Partial<Record<TradingType, boolean>>;
|
||||
countryCode: CountryCode | null;
|
||||
prefillFields: PrefillFields;
|
||||
settings: SuiteSettings;
|
||||
}
|
||||
@@ -183,6 +184,7 @@ const initialState: SuiteState = {
|
||||
sendForm: '',
|
||||
transactionHistory: '',
|
||||
},
|
||||
countryCode: null,
|
||||
settings: {
|
||||
theme: {
|
||||
variant: 'light',
|
||||
@@ -307,6 +309,9 @@ const suiteReducer = (state: SuiteState = initialState, action: Action): SuiteSt
|
||||
[action.tradingType]: true,
|
||||
};
|
||||
break;
|
||||
case SUITE.SET_COUNTRY_CODE:
|
||||
draft.countryCode = action.payload;
|
||||
break;
|
||||
case SUITE.SET_THEME:
|
||||
draft.settings.theme.variant = action.variant;
|
||||
break;
|
||||
@@ -528,6 +533,8 @@ export const selectIsFirmwareHashCheckEnabled = (state: SuiteRootState) =>
|
||||
export const selectIsFirmwareRevisionCheckEnabled = (state: SuiteRootState) =>
|
||||
state.suite.settings.enabledSecurityChecks.firmwareRevision;
|
||||
|
||||
export const selectCountryCode = (state: SuiteRootState) => state.suite.countryCode;
|
||||
|
||||
/**
|
||||
* Get firmware revision check error, or null if check was successful / skipped.
|
||||
*/
|
||||
|
||||
37
packages/suite/src/utils/suite/countryCode.ts
Normal file
37
packages/suite/src/utils/suite/countryCode.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
import { createThunk } from '@suite-common/redux-utils';
|
||||
import { CountryCode } from '@suite-common/trading';
|
||||
import { GEOLOCATION_API_URL } from '@trezor/urls';
|
||||
|
||||
import { setCountryCode } from 'src/actions/suite/suiteActions';
|
||||
|
||||
const GEOLOCATION_PREFIX = '@suite/geolocation';
|
||||
|
||||
type GeolocationResponse = {
|
||||
country: string;
|
||||
};
|
||||
|
||||
export const shouldFetchCountryCode = (routeName: string | undefined) => {
|
||||
if (!routeName) return false;
|
||||
|
||||
const isTradingRoute = routeName.includes('wallet-trading');
|
||||
const isStakingRoute = routeName.includes('staking');
|
||||
|
||||
return isTradingRoute || isStakingRoute;
|
||||
};
|
||||
|
||||
export const fetchCountryCodeThunk = createThunk<void, void, void>(
|
||||
`${GEOLOCATION_PREFIX}/fetchCountryCodeThunk`,
|
||||
async (_, { dispatch }) => {
|
||||
try {
|
||||
const response = await fetch(GEOLOCATION_API_URL);
|
||||
const data = (await response.json()) as GeolocationResponse;
|
||||
|
||||
if (typeof data?.country === 'string') {
|
||||
const code = data.country.trim().toUpperCase() as unknown as CountryCode;
|
||||
dispatch(setCountryCode(code));
|
||||
}
|
||||
} catch {
|
||||
// silently fail
|
||||
}
|
||||
},
|
||||
);
|
||||
@@ -173,3 +173,5 @@ export const ESHOP_KEEP_METAL_MULTI_SHARE_URL: Url =
|
||||
export const TRADING_DOWNLOAD_INVITY_APP_URL: Url = 'https://get.invity.io';
|
||||
|
||||
export const UNINSTALL_BRIDGE_URL: Url = 'https://trezor.io/learn/a/what-is-trezor-bridge';
|
||||
|
||||
export const GEOLOCATION_API_URL = 'https://suite-geolocation.admin9940.workers.dev/get-country/';
|
||||
|
||||
@@ -476,10 +476,274 @@
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"countryCodes": {
|
||||
"title": "Country code",
|
||||
"description": "Target users by country code (ISO 3166-1 alpha-2).",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/countryCodes"
|
||||
},
|
||||
"minItems": 1,
|
||||
"uniqueItems": true
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"countryCodes": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"AD",
|
||||
"AE",
|
||||
"AF",
|
||||
"AG",
|
||||
"AI",
|
||||
"AL",
|
||||
"AM",
|
||||
"AO",
|
||||
"AQ",
|
||||
"AR",
|
||||
"AS",
|
||||
"AT",
|
||||
"AU",
|
||||
"AW",
|
||||
"AX",
|
||||
"AZ",
|
||||
"BA",
|
||||
"BB",
|
||||
"BD",
|
||||
"BE",
|
||||
"BF",
|
||||
"BG",
|
||||
"BH",
|
||||
"BI",
|
||||
"BJ",
|
||||
"BL",
|
||||
"BM",
|
||||
"BN",
|
||||
"BO",
|
||||
"BQ",
|
||||
"BR",
|
||||
"BS",
|
||||
"BT",
|
||||
"BV",
|
||||
"BW",
|
||||
"BY",
|
||||
"BZ",
|
||||
"CA",
|
||||
"CC",
|
||||
"CD",
|
||||
"CF",
|
||||
"CG",
|
||||
"CH",
|
||||
"CI",
|
||||
"CK",
|
||||
"CL",
|
||||
"CM",
|
||||
"CN",
|
||||
"CO",
|
||||
"CR",
|
||||
"CU",
|
||||
"CV",
|
||||
"CW",
|
||||
"CX",
|
||||
"CY",
|
||||
"CZ",
|
||||
"DE",
|
||||
"DJ",
|
||||
"DK",
|
||||
"DM",
|
||||
"DO",
|
||||
"DZ",
|
||||
"EC",
|
||||
"EE",
|
||||
"EG",
|
||||
"EH",
|
||||
"ER",
|
||||
"ES",
|
||||
"ET",
|
||||
"FI",
|
||||
"FJ",
|
||||
"FK",
|
||||
"FM",
|
||||
"FO",
|
||||
"FR",
|
||||
"GA",
|
||||
"GB",
|
||||
"GD",
|
||||
"GE",
|
||||
"GF",
|
||||
"GG",
|
||||
"GH",
|
||||
"GI",
|
||||
"GL",
|
||||
"GM",
|
||||
"GN",
|
||||
"GP",
|
||||
"GQ",
|
||||
"GR",
|
||||
"GS",
|
||||
"GT",
|
||||
"GU",
|
||||
"GW",
|
||||
"GY",
|
||||
"HK",
|
||||
"HM",
|
||||
"HN",
|
||||
"HR",
|
||||
"HT",
|
||||
"HU",
|
||||
"ID",
|
||||
"IE",
|
||||
"IL",
|
||||
"IM",
|
||||
"IN",
|
||||
"IO",
|
||||
"IQ",
|
||||
"IR",
|
||||
"IS",
|
||||
"IT",
|
||||
"JE",
|
||||
"JM",
|
||||
"JO",
|
||||
"JP",
|
||||
"KE",
|
||||
"KG",
|
||||
"KH",
|
||||
"KI",
|
||||
"KM",
|
||||
"KN",
|
||||
"KP",
|
||||
"KR",
|
||||
"KW",
|
||||
"KY",
|
||||
"KZ",
|
||||
"LA",
|
||||
"LB",
|
||||
"LC",
|
||||
"LI",
|
||||
"LK",
|
||||
"LR",
|
||||
"LS",
|
||||
"LT",
|
||||
"LU",
|
||||
"LV",
|
||||
"LY",
|
||||
"MA",
|
||||
"MC",
|
||||
"MD",
|
||||
"ME",
|
||||
"MF",
|
||||
"MG",
|
||||
"MH",
|
||||
"MK",
|
||||
"ML",
|
||||
"MM",
|
||||
"MN",
|
||||
"MO",
|
||||
"MP",
|
||||
"MQ",
|
||||
"MR",
|
||||
"MS",
|
||||
"MT",
|
||||
"MU",
|
||||
"MV",
|
||||
"MW",
|
||||
"MX",
|
||||
"MY",
|
||||
"MZ",
|
||||
"NA",
|
||||
"NC",
|
||||
"NE",
|
||||
"NF",
|
||||
"NG",
|
||||
"NI",
|
||||
"NL",
|
||||
"NO",
|
||||
"NP",
|
||||
"NR",
|
||||
"NU",
|
||||
"NZ",
|
||||
"OM",
|
||||
"PA",
|
||||
"PE",
|
||||
"PF",
|
||||
"PG",
|
||||
"PH",
|
||||
"PK",
|
||||
"PL",
|
||||
"PM",
|
||||
"PN",
|
||||
"PR",
|
||||
"PS",
|
||||
"PT",
|
||||
"PW",
|
||||
"PY",
|
||||
"QA",
|
||||
"RE",
|
||||
"RO",
|
||||
"RS",
|
||||
"RU",
|
||||
"RW",
|
||||
"SA",
|
||||
"SB",
|
||||
"SC",
|
||||
"SD",
|
||||
"SE",
|
||||
"SG",
|
||||
"SH",
|
||||
"SI",
|
||||
"SJ",
|
||||
"SK",
|
||||
"SL",
|
||||
"SM",
|
||||
"SN",
|
||||
"SO",
|
||||
"SR",
|
||||
"SS",
|
||||
"ST",
|
||||
"SV",
|
||||
"SX",
|
||||
"SY",
|
||||
"SZ",
|
||||
"TC",
|
||||
"TD",
|
||||
"TF",
|
||||
"TG",
|
||||
"TH",
|
||||
"TJ",
|
||||
"TK",
|
||||
"TL",
|
||||
"TM",
|
||||
"TN",
|
||||
"TO",
|
||||
"TR",
|
||||
"TT",
|
||||
"TV",
|
||||
"TW",
|
||||
"TZ",
|
||||
"UA",
|
||||
"UG",
|
||||
"UM",
|
||||
"US",
|
||||
"UY",
|
||||
"UZ",
|
||||
"VA",
|
||||
"VC",
|
||||
"VE",
|
||||
"VG",
|
||||
"VI",
|
||||
"VN",
|
||||
"VU",
|
||||
"WF",
|
||||
"WS",
|
||||
"YE",
|
||||
"YT",
|
||||
"ZA",
|
||||
"ZM",
|
||||
"ZW"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,10 @@ import { Options } from '../messageSystemUtils';
|
||||
|
||||
const { getDeviceFeatures, getConnectDevice, getMessageSystemConfig } = testMocks;
|
||||
|
||||
const defaultOptions: Options = { settings: { tor: false, enabledNetworks: ['btc'] } };
|
||||
const defaultOptions: Options = {
|
||||
settings: { tor: false, enabledNetworks: ['btc'] },
|
||||
countryCode: 'US',
|
||||
};
|
||||
const defaultTransportsOption: TransportInfo = {
|
||||
type: 'BridgeTransport',
|
||||
apiType: 'usb',
|
||||
@@ -1428,7 +1431,7 @@ export const getValidMessages: GetValidMessagesFixture[] = [
|
||||
},
|
||||
],
|
||||
}),
|
||||
options: { settings: { tor: false, enabledNetworks: [] } },
|
||||
options: { settings: { tor: false, enabledNetworks: [] }, countryCode: 'US' },
|
||||
result: [getMessageSystemConfig().actions[1].message],
|
||||
},
|
||||
{
|
||||
@@ -1451,7 +1454,7 @@ export const getValidMessages: GetValidMessagesFixture[] = [
|
||||
},
|
||||
],
|
||||
}),
|
||||
options: { settings: { tor: false, enabledNetworks: [] } },
|
||||
options: { settings: { tor: false, enabledNetworks: [] }, countryCode: 'US' },
|
||||
result: [],
|
||||
},
|
||||
{
|
||||
@@ -1585,7 +1588,7 @@ export const getValidMessages: GetValidMessagesFixture[] = [
|
||||
},
|
||||
],
|
||||
}),
|
||||
options: { settings: { tor: false, enabledNetworks: [] } },
|
||||
options: { settings: { tor: false, enabledNetworks: [] }, countryCode: 'US' },
|
||||
result: [],
|
||||
},
|
||||
{
|
||||
@@ -1603,7 +1606,7 @@ export const getValidMessages: GetValidMessagesFixture[] = [
|
||||
},
|
||||
],
|
||||
}),
|
||||
options: { settings: { tor: false, enabledNetworks: ['btc'] } },
|
||||
options: { settings: { tor: false, enabledNetworks: ['btc'] }, countryCode: 'US' },
|
||||
result: [getMessageSystemConfig().actions[1].message],
|
||||
},
|
||||
{
|
||||
@@ -1627,6 +1630,7 @@ export const getValidMessages: GetValidMessagesFixture[] = [
|
||||
options: {
|
||||
settings: { tor: false, enabledNetworks: [] },
|
||||
transports: [{ ...defaultTransportsOption, version: '2.3.4' }],
|
||||
countryCode: 'US',
|
||||
},
|
||||
result: [getMessageSystemConfig().actions[1].message],
|
||||
},
|
||||
@@ -1651,6 +1655,7 @@ export const getValidMessages: GetValidMessagesFixture[] = [
|
||||
options: {
|
||||
settings: { tor: false, enabledNetworks: [] },
|
||||
transports: [{ ...defaultTransportsOption, version: '2.3.4' }],
|
||||
countryCode: 'US',
|
||||
},
|
||||
result: [],
|
||||
},
|
||||
@@ -1689,6 +1694,7 @@ export const getValidMessages: GetValidMessagesFixture[] = [
|
||||
options: {
|
||||
settings: { tor: false, enabledNetworks: [] },
|
||||
device: getConnectAcquiredDevice(),
|
||||
countryCode: 'US',
|
||||
},
|
||||
result: [getMessageSystemConfig().actions[1].message],
|
||||
},
|
||||
@@ -1719,6 +1725,7 @@ export const getValidMessages: GetValidMessagesFixture[] = [
|
||||
options: {
|
||||
settings: { tor: false, enabledNetworks: [] },
|
||||
device: getConnectAcquiredDevice(),
|
||||
countryCode: 'US',
|
||||
},
|
||||
result: [],
|
||||
},
|
||||
@@ -1753,6 +1760,7 @@ export const getValidMessages: GetValidMessagesFixture[] = [
|
||||
capabilities: ['Capability_Bitcoin'],
|
||||
}),
|
||||
},
|
||||
countryCode: 'US',
|
||||
},
|
||||
result: [getMessageSystemConfig().actions[1].message],
|
||||
},
|
||||
@@ -1787,6 +1795,7 @@ export const getValidMessages: GetValidMessagesFixture[] = [
|
||||
capabilities: ['Capability_Bitcoin_like'],
|
||||
}),
|
||||
},
|
||||
countryCode: 'US',
|
||||
},
|
||||
result: [getMessageSystemConfig().actions[1].message],
|
||||
},
|
||||
@@ -1803,6 +1812,7 @@ export const getValidMessages: GetValidMessagesFixture[] = [
|
||||
settings: { tor: true, enabledNetworks: ['btc'] },
|
||||
transports: [{ ...defaultTransportsOption, version: '2.0.30' }],
|
||||
device: getConnectAcquiredDevice(),
|
||||
countryCode: 'US',
|
||||
},
|
||||
result: getMessageSystemConfig().actions.map(action => action.message),
|
||||
},
|
||||
@@ -1843,6 +1853,7 @@ export const getValidMessages: GetValidMessagesFixture[] = [
|
||||
patch_version: 4,
|
||||
}),
|
||||
},
|
||||
countryCode: 'US',
|
||||
},
|
||||
result: [getMessageSystemConfig().actions[1].message],
|
||||
},
|
||||
@@ -1860,6 +1871,7 @@ export const getValidMessages: GetValidMessagesFixture[] = [
|
||||
settings: { tor: true, enabledNetworks: ['btc'] },
|
||||
transports: [{ ...defaultTransportsOption, version: '2.0.30' }],
|
||||
device: getConnectAcquiredDevice(),
|
||||
countryCode: 'US',
|
||||
},
|
||||
result: getMessageSystemConfig().actions.map(action => action.message),
|
||||
},
|
||||
@@ -1877,6 +1889,7 @@ export const getValidMessages: GetValidMessagesFixture[] = [
|
||||
settings: { tor: true, enabledNetworks: ['btc'] },
|
||||
transports: [{ ...defaultTransportsOption, version: '2.0.30' }],
|
||||
device: getConnectAcquiredDevice(),
|
||||
countryCode: 'US',
|
||||
},
|
||||
result: [getMessageSystemConfig().actions[1].message],
|
||||
},
|
||||
|
||||
@@ -19,6 +19,8 @@ const initialState: MessageSystemState = {
|
||||
dismissedMessages: {},
|
||||
|
||||
validExperiments: [],
|
||||
|
||||
countryCode: null,
|
||||
};
|
||||
|
||||
export const messageSystemPersistedWhitelist: Array<keyof MessageSystemState> = [
|
||||
|
||||
@@ -22,6 +22,8 @@ export const selectMessageSystemTimestamp = (state: MessageSystemRootState) =>
|
||||
export const selectMessageSystemCurrentSequence = (state: MessageSystemRootState) =>
|
||||
state.messageSystem.currentSequence;
|
||||
|
||||
export const selectCountryCode = (state: MessageSystemRootState) => state.messageSystem.countryCode;
|
||||
|
||||
const comparePriority = (a: Message, b: Message) => b.priority - a.priority;
|
||||
|
||||
const makeSelectActiveMessagesByCategory = (category: Category) =>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Category, ExperimentsItem, MessageSystem } from '@suite-common/suite-types';
|
||||
import { CountryCode } from '@suite-common/trading';
|
||||
|
||||
export type MessageState = { [key in Category]: boolean };
|
||||
|
||||
@@ -11,6 +12,7 @@ export type MessageSystemState = {
|
||||
[key: string]: MessageState;
|
||||
};
|
||||
validExperiments: string[];
|
||||
countryCode: CountryCode | null;
|
||||
};
|
||||
|
||||
export type MessageSystemRootState = {
|
||||
|
||||
@@ -12,6 +12,7 @@ import type {
|
||||
TrezorDevice,
|
||||
Version,
|
||||
} from '@suite-common/suite-types';
|
||||
import type { CountryCode } from '@suite-common/trading';
|
||||
import type { NetworkSymbol } from '@suite-common/wallet-config';
|
||||
import type { TransportInfo } from '@trezor/connect';
|
||||
import {
|
||||
@@ -64,6 +65,7 @@ export type Options = {
|
||||
settings: CurrentSettings;
|
||||
transports?: TransportInfo[];
|
||||
device?: TrezorDevice;
|
||||
countryCode: CountryCode | null;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -205,6 +207,19 @@ export const validateEnvironmentCompatibility = (
|
||||
);
|
||||
};
|
||||
|
||||
export const validateCountryCodeCompatibility = (
|
||||
allowedCountryCodes: CountryCode[],
|
||||
userCountryCode: CountryCode,
|
||||
): boolean => {
|
||||
if (!allowedCountryCodes.length) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return allowedCountryCodes.some(
|
||||
location => location.toUpperCase() === userCountryCode.toUpperCase(),
|
||||
);
|
||||
};
|
||||
|
||||
type EnvData = {
|
||||
osName: ReturnType<typeof getOsName>;
|
||||
osVersion: ReturnType<typeof transformVersionToSemverFormat>;
|
||||
@@ -228,7 +243,7 @@ export const getEnvData = async (): Promise<EnvData> => ({
|
||||
});
|
||||
|
||||
export const validateConditions = (condition: Condition, options: Options, envData: EnvData) => {
|
||||
const { device, transports = [], settings } = options;
|
||||
const { device, transports = [], settings, countryCode } = options;
|
||||
|
||||
const {
|
||||
duration: durationCondition,
|
||||
@@ -238,6 +253,7 @@ export const validateConditions = (condition: Condition, options: Options, envDa
|
||||
transport: transportCondition,
|
||||
settings: settingsCondition,
|
||||
devices: deviceCondition,
|
||||
countryCodes: countryCodeCondition,
|
||||
} = condition;
|
||||
|
||||
if (durationCondition && !validateDurationCompatibility(durationCondition)) {
|
||||
@@ -283,6 +299,13 @@ export const validateConditions = (condition: Condition, options: Options, envDa
|
||||
return false;
|
||||
}
|
||||
|
||||
if (
|
||||
countryCodeCondition &&
|
||||
(!countryCode || !validateCountryCodeCompatibility(countryCodeCondition, countryCode))
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
|
||||
@@ -15,6 +15,262 @@ export type FirmwareVariant = '*' | 'bitcoin-only' | 'regular';
|
||||
* Eligible authorized vendors.
|
||||
*/
|
||||
export type Vendor = '*' | 'trezor.io';
|
||||
export type CountryCodes =
|
||||
| 'AD'
|
||||
| 'AE'
|
||||
| 'AF'
|
||||
| 'AG'
|
||||
| 'AI'
|
||||
| 'AL'
|
||||
| 'AM'
|
||||
| 'AO'
|
||||
| 'AQ'
|
||||
| 'AR'
|
||||
| 'AS'
|
||||
| 'AT'
|
||||
| 'AU'
|
||||
| 'AW'
|
||||
| 'AX'
|
||||
| 'AZ'
|
||||
| 'BA'
|
||||
| 'BB'
|
||||
| 'BD'
|
||||
| 'BE'
|
||||
| 'BF'
|
||||
| 'BG'
|
||||
| 'BH'
|
||||
| 'BI'
|
||||
| 'BJ'
|
||||
| 'BL'
|
||||
| 'BM'
|
||||
| 'BN'
|
||||
| 'BO'
|
||||
| 'BQ'
|
||||
| 'BR'
|
||||
| 'BS'
|
||||
| 'BT'
|
||||
| 'BV'
|
||||
| 'BW'
|
||||
| 'BY'
|
||||
| 'BZ'
|
||||
| 'CA'
|
||||
| 'CC'
|
||||
| 'CD'
|
||||
| 'CF'
|
||||
| 'CG'
|
||||
| 'CH'
|
||||
| 'CI'
|
||||
| 'CK'
|
||||
| 'CL'
|
||||
| 'CM'
|
||||
| 'CN'
|
||||
| 'CO'
|
||||
| 'CR'
|
||||
| 'CU'
|
||||
| 'CV'
|
||||
| 'CW'
|
||||
| 'CX'
|
||||
| 'CY'
|
||||
| 'CZ'
|
||||
| 'DE'
|
||||
| 'DJ'
|
||||
| 'DK'
|
||||
| 'DM'
|
||||
| 'DO'
|
||||
| 'DZ'
|
||||
| 'EC'
|
||||
| 'EE'
|
||||
| 'EG'
|
||||
| 'EH'
|
||||
| 'ER'
|
||||
| 'ES'
|
||||
| 'ET'
|
||||
| 'FI'
|
||||
| 'FJ'
|
||||
| 'FK'
|
||||
| 'FM'
|
||||
| 'FO'
|
||||
| 'FR'
|
||||
| 'GA'
|
||||
| 'GB'
|
||||
| 'GD'
|
||||
| 'GE'
|
||||
| 'GF'
|
||||
| 'GG'
|
||||
| 'GH'
|
||||
| 'GI'
|
||||
| 'GL'
|
||||
| 'GM'
|
||||
| 'GN'
|
||||
| 'GP'
|
||||
| 'GQ'
|
||||
| 'GR'
|
||||
| 'GS'
|
||||
| 'GT'
|
||||
| 'GU'
|
||||
| 'GW'
|
||||
| 'GY'
|
||||
| 'HK'
|
||||
| 'HM'
|
||||
| 'HN'
|
||||
| 'HR'
|
||||
| 'HT'
|
||||
| 'HU'
|
||||
| 'ID'
|
||||
| 'IE'
|
||||
| 'IL'
|
||||
| 'IM'
|
||||
| 'IN'
|
||||
| 'IO'
|
||||
| 'IQ'
|
||||
| 'IR'
|
||||
| 'IS'
|
||||
| 'IT'
|
||||
| 'JE'
|
||||
| 'JM'
|
||||
| 'JO'
|
||||
| 'JP'
|
||||
| 'KE'
|
||||
| 'KG'
|
||||
| 'KH'
|
||||
| 'KI'
|
||||
| 'KM'
|
||||
| 'KN'
|
||||
| 'KP'
|
||||
| 'KR'
|
||||
| 'KW'
|
||||
| 'KY'
|
||||
| 'KZ'
|
||||
| 'LA'
|
||||
| 'LB'
|
||||
| 'LC'
|
||||
| 'LI'
|
||||
| 'LK'
|
||||
| 'LR'
|
||||
| 'LS'
|
||||
| 'LT'
|
||||
| 'LU'
|
||||
| 'LV'
|
||||
| 'LY'
|
||||
| 'MA'
|
||||
| 'MC'
|
||||
| 'MD'
|
||||
| 'ME'
|
||||
| 'MF'
|
||||
| 'MG'
|
||||
| 'MH'
|
||||
| 'MK'
|
||||
| 'ML'
|
||||
| 'MM'
|
||||
| 'MN'
|
||||
| 'MO'
|
||||
| 'MP'
|
||||
| 'MQ'
|
||||
| 'MR'
|
||||
| 'MS'
|
||||
| 'MT'
|
||||
| 'MU'
|
||||
| 'MV'
|
||||
| 'MW'
|
||||
| 'MX'
|
||||
| 'MY'
|
||||
| 'MZ'
|
||||
| 'NA'
|
||||
| 'NC'
|
||||
| 'NE'
|
||||
| 'NF'
|
||||
| 'NG'
|
||||
| 'NI'
|
||||
| 'NL'
|
||||
| 'NO'
|
||||
| 'NP'
|
||||
| 'NR'
|
||||
| 'NU'
|
||||
| 'NZ'
|
||||
| 'OM'
|
||||
| 'PA'
|
||||
| 'PE'
|
||||
| 'PF'
|
||||
| 'PG'
|
||||
| 'PH'
|
||||
| 'PK'
|
||||
| 'PL'
|
||||
| 'PM'
|
||||
| 'PN'
|
||||
| 'PR'
|
||||
| 'PS'
|
||||
| 'PT'
|
||||
| 'PW'
|
||||
| 'PY'
|
||||
| 'QA'
|
||||
| 'RE'
|
||||
| 'RO'
|
||||
| 'RS'
|
||||
| 'RU'
|
||||
| 'RW'
|
||||
| 'SA'
|
||||
| 'SB'
|
||||
| 'SC'
|
||||
| 'SD'
|
||||
| 'SE'
|
||||
| 'SG'
|
||||
| 'SH'
|
||||
| 'SI'
|
||||
| 'SJ'
|
||||
| 'SK'
|
||||
| 'SL'
|
||||
| 'SM'
|
||||
| 'SN'
|
||||
| 'SO'
|
||||
| 'SR'
|
||||
| 'SS'
|
||||
| 'ST'
|
||||
| 'SV'
|
||||
| 'SX'
|
||||
| 'SY'
|
||||
| 'SZ'
|
||||
| 'TC'
|
||||
| 'TD'
|
||||
| 'TF'
|
||||
| 'TG'
|
||||
| 'TH'
|
||||
| 'TJ'
|
||||
| 'TK'
|
||||
| 'TL'
|
||||
| 'TM'
|
||||
| 'TN'
|
||||
| 'TO'
|
||||
| 'TR'
|
||||
| 'TT'
|
||||
| 'TV'
|
||||
| 'TW'
|
||||
| 'TZ'
|
||||
| 'UA'
|
||||
| 'UG'
|
||||
| 'UM'
|
||||
| 'US'
|
||||
| 'UY'
|
||||
| 'UZ'
|
||||
| 'VA'
|
||||
| 'VC'
|
||||
| 'VE'
|
||||
| 'VG'
|
||||
| 'VI'
|
||||
| 'VN'
|
||||
| 'VU'
|
||||
| 'WF'
|
||||
| 'WS'
|
||||
| 'YE'
|
||||
| 'YT'
|
||||
| 'ZA'
|
||||
| 'ZM'
|
||||
| 'ZW';
|
||||
/**
|
||||
* Target users by country code (ISO 3166-1 alpha-2).
|
||||
*
|
||||
* @minItems 1
|
||||
*/
|
||||
export type CountryCode = CountryCodes[];
|
||||
export type Conditions = Condition[];
|
||||
export type Variant = 'info' | 'warning' | 'critical';
|
||||
export type Category = 'banner' | 'context' | 'modal' | 'feature';
|
||||
@@ -50,6 +306,7 @@ export interface Condition {
|
||||
*/
|
||||
settings?: Settings[];
|
||||
devices?: Device[];
|
||||
countryCodes?: CountryCode;
|
||||
}
|
||||
export interface Duration {
|
||||
from: DateTime;
|
||||
|
||||
@@ -3,7 +3,7 @@ import { isArrayMember } from '@trezor/utils';
|
||||
class Regional {
|
||||
readonly UNKNOWN_COUNTRY = 'unknown';
|
||||
|
||||
countries: [string, string][] = [
|
||||
countries = [
|
||||
[this.UNKNOWN_COUNTRY, `🌍 Worldwide`],
|
||||
['AD', '🇦🇩 Andorra'],
|
||||
['AE', '🇦🇪 United Arab Emirates'],
|
||||
@@ -254,7 +254,7 @@ class Regional {
|
||||
['ZA', '🇿🇦 South Africa'],
|
||||
['ZM', '🇿🇲 Zambia'],
|
||||
['ZW', '🇿🇼 Zimbabwe'],
|
||||
];
|
||||
] as const;
|
||||
|
||||
countriesMap = new Map<string, string>(this.countries);
|
||||
|
||||
@@ -309,6 +309,12 @@ class Regional {
|
||||
}
|
||||
}
|
||||
|
||||
type EEACountryCodes = (typeof regional.EEACountryCodes)[number];
|
||||
|
||||
export const regional = new Regional();
|
||||
|
||||
export type EEACountryCodes = (typeof regional.EEACountryCodes)[number];
|
||||
|
||||
export type OriginalCountryCode = (typeof regional.countries)[number][0];
|
||||
|
||||
// Add Cloudflare-specific codes "XX" (no country data) and "T1" (Tor network).
|
||||
// See: https://developers.cloudflare.com/fundamentals/reference/http-headers/#cf-ipcountry
|
||||
export type CountryCode = Exclude<OriginalCountryCode, 'unknown'> | 'XX' | 'T1';
|
||||
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
getValidExperimentIds,
|
||||
getValidMessages,
|
||||
messageSystemActions,
|
||||
selectCountryCode,
|
||||
selectMessageSystemConfig,
|
||||
} from '@suite-common/message-system';
|
||||
import { createMiddleware } from '@suite-common/redux-utils';
|
||||
@@ -28,6 +29,7 @@ export const messageSystemMiddleware = createMiddleware(
|
||||
const config = selectMessageSystemConfig(getState());
|
||||
const device = selectSelectedDevice(getState());
|
||||
const enabledNetworks = selectDeviceEnabledDiscoveryNetworkSymbols(getState());
|
||||
const countryCode = selectCountryCode(getState());
|
||||
|
||||
const validationParams = {
|
||||
device,
|
||||
@@ -35,6 +37,7 @@ export const messageSystemMiddleware = createMiddleware(
|
||||
tor: false, // not supported in suite-native
|
||||
enabledNetworks,
|
||||
},
|
||||
countryCode,
|
||||
};
|
||||
const [validMessages, validExperimentIds] = await Promise.all([
|
||||
getValidMessages(config, validationParams),
|
||||
|
||||
@@ -89,6 +89,7 @@ const getPreloadedState = ({
|
||||
},
|
||||
dismissedMessages: {},
|
||||
validExperiments: [],
|
||||
countryCode: null,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user