Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 63 additions & 0 deletions app/actions/notification/helpers/index.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
enablePushNotifications,
disablePushNotifications,
hasNotificationPreferences,
setMarketingNotificationPreferencesEnabled,
type setContentPreviewToken as setContentPreviewTokenFn,
type getContentPreviewToken as getContentPreviewTokenFn,
type subscribeToContentPreviewToken as subscribeToContentPreviewTokenFn,
Expand Down Expand Up @@ -64,6 +65,13 @@ describe('helpers - enableNotificationServices()', () => {
Engine.context.NotificationServicesController.enableMetamaskNotifications,
).toHaveBeenCalledWith(options);
});

it('forwards enable notification options', async () => {
await enableNotifications({ registerPushNotifications: false });
expect(
Engine.context.NotificationServicesController.enableMetamaskNotifications,
).toHaveBeenCalledWith({ registerPushNotifications: false });
});
});

describe('helpers - hasNotificationPreferences()', () => {
Expand All @@ -89,6 +97,61 @@ describe('helpers - hasNotificationPreferences()', () => {
});
});

describe('helpers - setMarketingNotificationPreferencesEnabled()', () => {
it('updates marketing notification preferences when AUS preferences exist', async () => {
const preferences = {
walletActivity: {
inAppNotificationsEnabled: true,
pushNotificationsEnabled: true,
accounts: [],
},
marketing: {
inAppNotificationsEnabled: false,
pushNotificationsEnabled: false,
},
perps: {
inAppNotificationsEnabled: true,
pushNotificationsEnabled: true,
},
socialAI: {
inAppNotificationsEnabled: true,
pushNotificationsEnabled: true,
txAmountLimit: 500,
mutedTraderProfileIds: [],
},
};
jest.mocked(Engine.controllerMessenger.call).mockResolvedValue(preferences);

await setMarketingNotificationPreferencesEnabled(true);

expect(Engine.controllerMessenger.call).toHaveBeenCalledWith(
'AuthenticatedUserStorageService:getNotificationPreferences',
);
expect(Engine.controllerMessenger.call).toHaveBeenCalledWith(
'AuthenticatedUserStorageService:putNotificationPreferences',
{
...preferences,
marketing: {
inAppNotificationsEnabled: true,
pushNotificationsEnabled: true,
},
},
'mobile',
);
});

it('does not persist when AUS preferences are missing', async () => {
jest.mocked(Engine.controllerMessenger.call).mockResolvedValue(null);

await setMarketingNotificationPreferencesEnabled(true);

expect(Engine.controllerMessenger.call).toHaveBeenCalledTimes(1);
expect(Engine.controllerMessenger.call).toHaveBeenCalledWith(
'AuthenticatedUserStorageService:getNotificationPreferences',
);
});
});

describe('helpers - disableNotificationServices()', () => {
it('invoke notification services method', async () => {
await disableNotifications();
Expand Down
34 changes: 33 additions & 1 deletion app/actions/notification/helpers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ import type {
import Engine from '../../../core/Engine';
import { isNotificationsFeatureEnabled } from '../../../util/notifications';

const CLIENT_TYPE = 'mobile' as const;
const GET_NOTIFICATION_PREFERENCES_ACTION =
'AuthenticatedUserStorageService:getNotificationPreferences' as const;
const PUT_NOTIFICATION_PREFERENCES_ACTION =
'AuthenticatedUserStorageService:putNotificationPreferences' as const;

const previewTokenEventEmitter = new EventEmitter2();
const PREVIEW_TOKEN_UPDATE_EVENT = 'previewTokenUpdate';
let previewToken: string | undefined;
Expand Down Expand Up @@ -57,11 +63,37 @@ export const enableNotifications = async (
export const hasNotificationPreferences = async () => {
assertIsFeatureEnabled();
const preferences = await Engine.controllerMessenger.call(
'AuthenticatedUserStorageService:getNotificationPreferences',
GET_NOTIFICATION_PREFERENCES_ACTION,
);
return preferences != null;
};

export const setMarketingNotificationPreferencesEnabled = async (
isEnabled: boolean,
) => {
assertIsFeatureEnabled();
const preferences = await Engine.controllerMessenger.call(
GET_NOTIFICATION_PREFERENCES_ACTION,
);

if (!preferences) {
return;
}

await Engine.controllerMessenger.call(
PUT_NOTIFICATION_PREFERENCES_ACTION,
{
...preferences,
marketing: {
...preferences.marketing,
inAppNotificationsEnabled: isEnabled,
pushNotificationsEnabled: isEnabled,
},
},
CLIENT_TYPE,
);
};

/**
* Disable Notifications Switch
* - Disables wallet notifications, feature announcements, and push notifications
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,7 @@ const styleSheet = (params: {
const { colors } = params.theme;

return StyleSheet.create({
base: Object.assign(
{
padding: 16,
} as ViewStyle,
style,
) as ViewStyle,
base: Object.assign({} as ViewStyle, style) as ViewStyle,
cellBase: Object.assign(
{
flexDirection: 'row',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@

// Third library dependencies.
import React from 'react';
import { TouchableOpacity, TouchableWithoutFeedback, View } from 'react-native';
import { View } from 'react-native';

// External dependencies.
import { useStyles } from '../../hooks';
import Pressable from '../Pressable';
import Tag from '../../../component-library/components/Tags/Tag';

// Internal dependencies.
Expand Down Expand Up @@ -74,27 +75,25 @@ const CellSelectWithMenu = ({
)}
{!!secondaryText &&
(props.onTextClick ? (
<TouchableWithoutFeedback>
<TouchableOpacity
style={styles.containerRow}
onPress={props.onTextClick}
<Pressable
style={styles.containerRow}
onPress={props.onTextClick}
>
<Text
numberOfLines={1}
variant={DEFAULT_CELLBASE_AVATAR_SECONDARYTEXT_TEXTVARIANT}
style={styles.secondaryText}
>
<Text
numberOfLines={1}
variant={DEFAULT_CELLBASE_AVATAR_SECONDARYTEXT_TEXTVARIANT}
style={styles.secondaryText}
>
{secondaryText}
</Text>
{showSecondaryTextIcon && (
<Icon
name={IconName.ArrowDown}
size={IconSize.Xss}
style={styles.arrowStyle}
/>
)}
</TouchableOpacity>
</TouchableWithoutFeedback>
{secondaryText}
</Text>
{showSecondaryTextIcon && (
<Icon
name={IconName.ArrowDown}
size={IconSize.Xss}
style={styles.arrowStyle}
/>
)}
</Pressable>
) : (
<View style={styles.containerRow}>
<Text
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { ListItemMultiSelectButtonProps } from './ListItemMultiSelectButton.type
export const DEFAULT_LISTITEMMULTISELECT_GAP = 16;
export const BUTTON_TEST_ID = 'button-menu-select-test-id';
export const BUTTON_TEXT_TEST_ID = 'button-text-select-test-id';
export const ROW_TEST_ID = 'list-item-multi-select-button-row';

// Sample consts
export const SAMPLE_LISTITEMMULTISELECT_PROPS = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,16 +24,10 @@ const styleSheet = (params: {
const { colors } = theme;
const { style, isDisabled, isSelected } = vars;
return StyleSheet.create({
base: Object.assign(
{
flex: 1,
position: 'relative',
opacity: isDisabled ? 0.5 : 1,
padding: 16,
zIndex: 1,
} as ViewStyle,
style,
) as ViewStyle,
base: {
flex: 1,
padding: 16,
} as ViewStyle,
listItem: {
paddingRight: 0,
paddingTop: 0,
Expand All @@ -47,21 +41,24 @@ const styleSheet = (params: {
paddingLeft: 0,
paddingTop: 0,
paddingBottom: 0,
zIndex: 2,
},
containerRow: {
flexDirection: 'row',
alignItems: 'flex-start',
marginBottom: 0,
marginLeft: 40,
},
container: {
backgroundColor: isSelected
? colors.background.muted
: getElevatedSurfaceColor(theme),
flexDirection: 'row',
alignItems: 'center',
},
container: Object.assign(
{
backgroundColor: isSelected
? colors.background.muted
: getElevatedSurfaceColor(theme),
flexDirection: 'row',
alignItems: 'center',
opacity: isDisabled ? 0.5 : 1,
} as ViewStyle,
style,
) as ViewStyle,
itemColumn: {
display: 'flex',
marginTop: 0,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,36 @@ import { View } from 'react-native';
// Internal dependencies.
import ListItemMultiSelectButton from './ListItemMultiSelectButton';
import { IconName } from '../../../component-library/components/Icons/Icon';
import { BUTTON_TEST_ID } from './ListItemMultiSelectButton.constants';
import {
BUTTON_TEST_ID,
ROW_TEST_ID,
} from './ListItemMultiSelectButton.constants';

describe('ListItemMultiSelectButton', () => {
it('renders with default props', () => {
const { getByRole } = render(
const { getByTestId } = render(
<ListItemMultiSelectButton>
<View />
</ListItemMultiSelectButton>,
);

expect(getByTestId(ROW_TEST_ID)).toBeOnTheScreen();
});

it('exposes accessibilityRole="button" on the row', () => {
const { getByTestId } = render(
<ListItemMultiSelectButton>
<View />
</ListItemMultiSelectButton>,
);

expect(getByRole('button')).toBeOnTheScreen();
expect(getByTestId(ROW_TEST_ID).props.accessibilityRole).toBe('button');
});

it('calls onPress when the button is pressed', () => {
const mockOnPress = jest.fn();

const { getByRole } = render(
const { getByTestId } = render(
<ListItemMultiSelectButton
onPress={mockOnPress}
buttonProps={{ onButtonClick: mockOnPress }}
Expand All @@ -31,7 +44,7 @@ describe('ListItemMultiSelectButton', () => {
</ListItemMultiSelectButton>,
);

fireEvent.press(getByRole('button'));
fireEvent.press(getByTestId(ROW_TEST_ID));

expect(mockOnPress).toHaveBeenCalled();
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@

// Third party dependencies.
import React from 'react';
import { TouchableOpacity, View } from 'react-native';
import { View } from 'react-native';

// External dependencies.
import { useStyles } from '../../hooks';
import Pressable from '../Pressable';
import ListItem from '../../../component-library/components/List/ListItem/ListItem';

// Internal dependencies.
Expand All @@ -14,6 +15,7 @@ import { ListItemMultiSelectButtonProps } from './ListItemMultiSelectButton.type
import {
BUTTON_TEST_ID,
DEFAULT_LISTITEMMULTISELECT_GAP,
ROW_TEST_ID,
} from './ListItemMultiSelectButton.constants';
import ButtonIcon from '../../../component-library/components/Buttons/ButtonIcon';
import {
Expand Down Expand Up @@ -46,18 +48,19 @@ const ListItemMultiSelectButton: React.FC<ListItemMultiSelectButtonProps> = ({
});

return (
<View style={styles.container}>
<TouchableOpacity
style={styles.base}
disabled={isDisabled}
onPress={props.onPress}
onLongPress={props.onPress}
{...props}
>
<Pressable
testID={ROW_TEST_ID}
style={styles.container}
disabled={isDisabled}
onPress={props.onPress}
onLongPress={props.onPress}
{...props}
>
<View style={styles.base}>
<ListItem gap={gap} style={styles.containerColumn}>
{children}
</ListItem>
</TouchableOpacity>
</View>
{showButtonIcon ? (
<View style={styles.buttonIcon}>
<ButtonIcon
Expand All @@ -81,7 +84,7 @@ const ListItemMultiSelectButton: React.FC<ListItemMultiSelectButtonProps> = ({
/>
</View>
) : null}
</View>
</Pressable>
);
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export const DEFAULT_LIST_ITEM_MULTISELECT_WITH_MENU_BUTTON_GAP = 16;
export const BUTTON_TEST_ID = 'button-menu-select-with-menu-button-test-id';
export const BUTTON_TEXT_TEST_ID =
'button-text-select-with-menu-button-test-id';
export const ROW_TEST_ID = 'list-item-multi-select-with-menu-button-row';

// Sample consts
export const SAMPLE_LIST_ITEM_MULTISELECT_WITH_MENU_BUTTON_PROPS = {
Expand Down
Loading
Loading