Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
18 changes: 15 additions & 3 deletions src/components/notification/view/notificationView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,26 @@ import { selectIsDarkTheme } from '../../../redux/selectors';
import styles from './notificationStyles';

// Each `key` must match a NotificationFilters enum value (passed straight to the
// notifications query) and have a `notification.<key>` locale string, which is
// also the displayed label — so the key is the single source for both.
// notifications query) and have a `notification.filters.<key>` locale string.
// Labels live under their own `filters` namespace rather than reusing
// `notification.<key>`: several of those are notification BODY strings
// (`payouts` is "Payout received: ${amount}", `delegations` is "delegated"), so
// using them as labels renders template text in the dropdown.
const FILTERS = [
{ key: 'activities' },
{ key: 'replies' },
{ key: 'mentions' },
{ key: 'rvotes' },
{ key: 'follows' },
{ key: 'reblogs' },
{ key: 'transfers' },
{ key: 'delegations' },
{ key: 'nfavorites' },
{ key: 'nbookmarks' },
{ key: 'payouts' },
{ key: 'scheduled_published' },
{ key: 'account_updates' },
{ key: 'weekly_earnings' },
];

interface Props {
Expand Down Expand Up @@ -113,7 +125,7 @@ const NotificationView = ({
<FilterBar
dropdownIconName="arrow-drop-down"
options={FILTERS.map((item) =>
intl.formatMessage({ id: `notification.${item.key}` }).toUpperCase(),
intl.formatMessage({ id: `notification.filters.${item.key}` }).toUpperCase(),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Translated Labels Lose Their Fallback

This lookup moves every filter label to notification.filters.*, but only the English locale defines that namespace. With another supported locale selected, these calls have no defaultMessage, so the filter bar can display uppercased message IDs such as NOTIFICATION.FILTERS.REPLIES instead of readable labels.

Fix in Claude Code

)}
defaultText="ALL"
onDropdownSelect={_handleOnDropdownSelect}
Expand Down
19 changes: 19 additions & 0 deletions src/config/locales/en-US.json
Original file line number Diff line number Diff line change
Expand Up @@ -373,6 +373,22 @@
"selected": "Selected {postfix}"
},
"notification": {
"filters": {
"activities": "All",
"replies": "Replies",
"mentions": "Mentions",
"rvotes": "Votes",
"follows": "Follows",
"reblogs": "Reblogs",
"transfers": "Transfers",
"delegations": "Delegations",
"nfavorites": "Favorites",
"nbookmarks": "Bookmarks",
"payouts": "Payouts",
"scheduled_published": "Scheduled posts",
"account_updates": "Account updates",
"weekly_earnings": "Weekly earnings"
},
"this_week": "This Week",
"reblog": "reblogged",
"unfollow": "unfollowed you",
Expand Down Expand Up @@ -1581,6 +1597,9 @@
"vote": "Vote",
"follow": "Follow",
"scheduled_published": "Scheduled post published",
"payouts": "Payouts",
"account_update": "Account updates",
"weekly_earnings": "Weekly earnings",
"fcm_unavailable": "Push notifications are not supported on this device. You will receive notifications only while the app is open."
},
"keys_warning": "Sensitive account information!\nEcency must be secured with PIN code before viewing private keys.",
Expand Down
6 changes: 6 additions & 0 deletions src/providers/ecency/ecency.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,12 @@ export enum NotificationFilters {
DELEGATIONS = 'delegations',
FAVOURITES = 'nfavorites',
BOOKMARKS = 'nbookmarks',
PAYOUTS = 'payouts',
SCHEDULED_PUBLISHED = 'scheduled_published',
// Filter path is plural while the notification `type` string is singular
// (`account_update`) — enotify routes on the plural form.
ACCOUNT_UPDATES = 'account_updates',
WEEKLY_EARNINGS = 'weekly_earnings',
}

export enum PointActivityIds {
Expand Down
28 changes: 28 additions & 0 deletions src/redux/actions/applicationActions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ import {
CHANGE_REBLOG_NOTIFICATION,
CHANGE_TRANSFERS_NOTIFICATION,
CHANGE_SCHEDULED_PUBLISHED_NOTIFICATION,
CHANGE_DELEGATIONS_NOTIFICATION,
CHANGE_PAYOUTS_NOTIFICATION,
CHANGE_ACCOUNT_UPDATE_NOTIFICATION,
CHANGE_WEEKLY_EARNINGS_NOTIFICATION,
CHANGE_ALL_NOTIFICATION_SETTINGS,
CHANGE_VOTE_NOTIFICATION,
IS_CONNECTED,
Expand Down Expand Up @@ -140,6 +144,30 @@ export const changeNotificationSettings = (payload) => {
type: CHANGE_SCHEDULED_PUBLISHED_NOTIFICATION,
};

case 'notification.delegations':
return {
payload: payload.action,
type: CHANGE_DELEGATIONS_NOTIFICATION,
};

case 'notification.payouts':
return {
payload: payload.action,
type: CHANGE_PAYOUTS_NOTIFICATION,
};

case 'notification.accountUpdate':
return {
payload: payload.action,
type: CHANGE_ACCOUNT_UPDATE_NOTIFICATION,
};

case 'notification.weeklyEarnings':
return {
payload: payload.action,
type: CHANGE_WEEKLY_EARNINGS_NOTIFICATION,
};

case 'notification':
return {
payload: payload.action,
Expand Down
4 changes: 4 additions & 0 deletions src/redux/constants/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@ export const CHANGE_BOOKMARK_NOTIFICATION = 'CHANGE_BOOKMARK_NOTIFICATION';
export const CHANGE_REBLOG_NOTIFICATION = 'CHANGE_REBLOG_NOTIFICATION';
export const CHANGE_TRANSFERS_NOTIFICATION = 'CHANGE_TRANSFERS_NOTIFICATION';
export const CHANGE_SCHEDULED_PUBLISHED_NOTIFICATION = 'CHANGE_SCHEDULED_PUBLISHED_NOTIFICATION';
export const CHANGE_DELEGATIONS_NOTIFICATION = 'CHANGE_DELEGATIONS_NOTIFICATION';
export const CHANGE_PAYOUTS_NOTIFICATION = 'CHANGE_PAYOUTS_NOTIFICATION';
export const CHANGE_ACCOUNT_UPDATE_NOTIFICATION = 'CHANGE_ACCOUNT_UPDATE_NOTIFICATION';
export const CHANGE_WEEKLY_EARNINGS_NOTIFICATION = 'CHANGE_WEEKLY_EARNINGS_NOTIFICATION';
export const CHANGE_ALL_NOTIFICATION_SETTINGS = 'CHANGE_ALL_NOTIFICATION_SETTINGS';
export const SET_LAST_APP_VERSION = 'SET_LAST_APP_VERSION';
export const SET_COLOR_THEME = 'SET_COLOR_THEME';
Expand Down
40 changes: 40 additions & 0 deletions src/redux/reducers/applicationReducer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ import {
CHANGE_REBLOG_NOTIFICATION,
CHANGE_TRANSFERS_NOTIFICATION,
CHANGE_SCHEDULED_PUBLISHED_NOTIFICATION,
CHANGE_DELEGATIONS_NOTIFICATION,
CHANGE_PAYOUTS_NOTIFICATION,
CHANGE_ACCOUNT_UPDATE_NOTIFICATION,
CHANGE_WEEKLY_EARNINGS_NOTIFICATION,
CHANGE_VOTE_NOTIFICATION,
CHANGE_ALL_NOTIFICATION_SETTINGS,
IS_CONNECTED,
Expand Down Expand Up @@ -76,6 +80,10 @@ interface State {
transfersNotification: boolean;
voteNotification: boolean;
scheduledPublishedNotification: boolean;
delegationsNotification: boolean;
payoutsNotification: boolean;
accountUpdateNotification: boolean;
weeklyEarningsNotification: boolean;
Comment on lines +83 to +86

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Include the new fields in the bulk notification-settings reducer.

CHANGE_ALL_NOTIFICATION_SETTINGS at Lines 289-308 does not update these four fields. Any “enable/disable all notifications” action will leave delegations, payouts, account updates, and weekly earnings unchanged. Preserve explicit false values for legacy payloads.

Suggested fix
         notificationDetails: {
           ...state.notificationDetails,
+          delegationsNotification:
+            action.payload.delegationsNotification ??
+            state.notificationDetails.delegationsNotification,
+          payoutsNotification:
+            action.payload.payoutsNotification ??
+            state.notificationDetails.payoutsNotification,
+          accountUpdateNotification:
+            action.payload.accountUpdateNotification ??
+            state.notificationDetails.accountUpdateNotification,
+          weeklyEarningsNotification:
+            action.payload.weeklyEarningsNotification ??
+            state.notificationDetails.weeklyEarningsNotification,
           mentionNotification: action.payload.mentionNotification,
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/redux/reducers/applicationReducer.ts` around lines 83 - 86, Update the
CHANGE_ALL_NOTIFICATION_SETTINGS reducer case to include
delegationsNotification, payoutsNotification, accountUpdateNotification, and
weeklyEarningsNotification in the bulk update. Use explicit false-preserving
payload handling so legacy payloads that provide false remain false while
unspecified fields retain their existing behavior.

};
postUpvotePercent: number;
commentUpvotePercent: number;
Expand Down Expand Up @@ -122,6 +130,10 @@ const initialState: State = {
transfersNotification: true,
voteNotification: true,
scheduledPublishedNotification: true,
delegationsNotification: true,
payoutsNotification: true,
accountUpdateNotification: true,
weeklyEarningsNotification: true,
},
postUpvotePercent: 1,
commentUpvotePercent: 1,
Expand Down Expand Up @@ -238,6 +250,34 @@ const applicationReducer = (state = initialState, action): State => {
scheduledPublishedNotification: action.payload,
},
});
case CHANGE_DELEGATIONS_NOTIFICATION:
return Object.assign({}, state, {
notificationDetails: {
...state.notificationDetails,
delegationsNotification: action.payload,
},
});
case CHANGE_PAYOUTS_NOTIFICATION:
return Object.assign({}, state, {
notificationDetails: {
...state.notificationDetails,
payoutsNotification: action.payload,
},
});
case CHANGE_ACCOUNT_UPDATE_NOTIFICATION:
return Object.assign({}, state, {
notificationDetails: {
...state.notificationDetails,
accountUpdateNotification: action.payload,
},
});
case CHANGE_WEEKLY_EARNINGS_NOTIFICATION:
return Object.assign({}, state, {
notificationDetails: {
...state.notificationDetails,
weeklyEarningsNotification: action.payload,
},
});
case CHANGE_VOTE_NOTIFICATION:
return Object.assign({}, state, {
notificationDetails: {
Expand Down
2 changes: 1 addition & 1 deletion src/redux/store/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ const persistConfig = {
key: 'root',
// Storage Method (React Native)
storage: AsyncStorage,
version: 20, // v20: Default scheduledPublished notification ON, backfill bookmark setting
version: 21, // v21: Default delegations/payouts/accountUpdate/weeklyEarnings notifications ON
// // Blacklist (Don't Save Specific Reducers)
blacklist: ['communities', 'user', 'ui'],
transforms: [transformCacheVoteMap, transformWalkthroughMap],
Expand Down
4 changes: 4 additions & 0 deletions src/screens/application/container/applicationContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1078,6 +1078,10 @@ class ApplicationContainer extends Component {
transfersNotification: 6,
favoriteNotification: 13,
bookmarkNotification: 15,
delegationsNotification: 10,
payoutsNotification: 19,
accountUpdateNotification: 20,
weeklyEarningsNotification: 21,
Comment thread
coderabbitai[bot] marked this conversation as resolved.
scheduledPublishedNotification: 22,
};

Expand Down
4 changes: 4 additions & 0 deletions src/screens/login/container/loginContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,10 @@ class LoginContainer extends PureComponent {
transfers: 6,
favorite: 13,
bookmark: 15,
delegations: 10,
payouts: 19,
accountUpdate: 20,
weeklyEarnings: 21,
scheduledPublished: 22,
};
const notifyTypes = [];
Expand Down
8 changes: 8 additions & 0 deletions src/screens/settings/container/settingsContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -538,6 +538,10 @@ class SettingsContainer extends Component {
transfers: 6,
favorite: 13,
bookmark: 15,
delegations: 10,
payouts: 19,
accountUpdate: 20,
weeklyEarnings: 21,
scheduledPublished: 22,
};
const notifyTypes = [];
Expand Down Expand Up @@ -888,6 +892,10 @@ const mapStateToProps = (state) => {
transfersNotification: notificationDetails.transfersNotification,
voteNotification: notificationDetails.voteNotification,
scheduledPublishedNotification: notificationDetails.scheduledPublishedNotification,
delegationsNotification: notificationDetails.delegationsNotification,
payoutsNotification: notificationDetails.payoutsNotification,
accountUpdateNotification: notificationDetails.accountUpdateNotification,
weeklyEarningsNotification: notificationDetails.weeklyEarningsNotification,
selectedApi: selectApi(state),
selectedCurrency: selectCurrency(state),
selectedLanguage: selectLanguage(state),
Expand Down
40 changes: 40 additions & 0 deletions src/screens/settings/screen/settingsScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,10 @@ const SettingsScreen = ({
transfersNotification,
voteNotification,
scheduledPublishedNotification,
delegationsNotification,
payoutsNotification,
accountUpdateNotification,
weeklyEarningsNotification,
handleOnButtonPress,
isLoading,
isHideImages,
Expand Down Expand Up @@ -347,6 +351,42 @@ const SettingsScreen = ({
isOn={scheduledPublishedNotification}
handleOnChange={handleOnChange}
/>
<SettingsItem
title={intl.formatMessage({
id: 'settings.notification.delegations',
})}
type="toggle"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 New Toggles Skip Update Path

When a user changes any of the four new toggles, its action type reaches _handleToggleChanged, which has no matching case and falls through to default. _handleNotification never runs, so Redux and device registration remain unchanged and the toggle snaps back.

Fix in Claude Code

actionType="notification.delegations"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Wire new notification toggles through the settings handler

When a user changes this new toggle (and the three new toggles that follow), SettingsItem passes the actionType to handleOnChange, but SettingsContainer._handleToggleChanged only forwards notification action types through notification.scheduledPublished; notification.delegations, notification.payouts, notification.accountUpdate, and notification.weeklyEarnings all fall through the default case. In that scenario no Redux action is dispatched and _setPushToken is never called, so users cannot actually mute these newly added push types from Settings.

Useful? React with 👍 / 👎.

isOn={delegationsNotification}
handleOnChange={handleOnChange}
/>
<SettingsItem
title={intl.formatMessage({
id: 'settings.notification.payouts',
})}
type="toggle"
actionType="notification.payouts"
isOn={payoutsNotification}
handleOnChange={handleOnChange}
/>
<SettingsItem
title={intl.formatMessage({
id: 'settings.notification.account_update',
})}
type="toggle"
actionType="notification.accountUpdate"
isOn={accountUpdateNotification}
handleOnChange={handleOnChange}
/>
<SettingsItem
title={intl.formatMessage({
id: 'settings.notification.weekly_earnings',
})}
type="toggle"
actionType="notification.weeklyEarnings"
isOn={weeklyEarningsNotification}
handleOnChange={handleOnChange}
/>
Comment on lines +354 to +389

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Route the four new notification toggle actions.

These controls emit action types that SettingsContainer._handleToggleChanged does not handle (Line 472-483), so toggling them falls through default; Redux state and push-token settings are never updated.

Suggested handler addition
       case 'notification.scheduledPublished':
+      case 'notification.delegations':
+      case 'notification.payouts':
+      case 'notification.accountUpdate':
+      case 'notification.weeklyEarnings':
         this._handleNotification(action, actionType);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/screens/settings/screen/settingsScreen.tsx` around lines 354 - 389,
Update SettingsContainer._handleToggleChanged to handle
notification.delegations, notification.payouts, notification.accountUpdate, and
notification.weeklyEarnings emitted by the four SettingsItem controls,
dispatching the corresponding Redux and push-token setting updates instead of
falling through default. Preserve existing toggle handling for all other action
types.

</CollapsibleCard>
{isFCMAvailable === false && (
<View
Expand Down
30 changes: 30 additions & 0 deletions src/utils/migrationHelpers.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -424,6 +424,36 @@ describe('reduxMigrations', () => {
});
});

describe('v21: delegations/payouts/accountUpdate/weeklyEarnings notification defaults', () => {
it('defaults the four newly-toggleable notification settings ON when missing', () => {
const state = { application: { notificationDetails: { voteNotification: false } } } as any;
const result = reduxMigrations[21](state);
const details = result.application.notificationDetails;
expect(details.delegationsNotification).toBe(true);
expect(details.payoutsNotification).toBe(true);
expect(details.accountUpdateNotification).toBe(true);
expect(details.weeklyEarningsNotification).toBe(true);
// existing settings are untouched
expect(details.voteNotification).toBe(false);
});

it('preserves an explicit false', () => {
const state = {
application: { notificationDetails: { payoutsNotification: false } },
} as any;
const result = reduxMigrations[21](state);
expect(result.application.notificationDetails.payoutsNotification).toBe(false);
expect(result.application.notificationDetails.delegationsNotification).toBe(true);
});

it('is a no-op when notificationDetails is absent', () => {
const state = { application: {}, account: { keep: 'me' } } as any;
const result = reduxMigrations[21](state);
expect(result.application.notificationDetails).toBeUndefined();
expect(result.account.keep).toBe('me');
});
});

describe('failure isolation', () => {
it('rolls back a throwing migration and preserves the rest of the state', () => {
// v1 writes state.application.notificationDetails.favoriteNotification; with
Expand Down
14 changes: 14 additions & 0 deletions src/utils/migrationHelpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -556,6 +556,20 @@ const reduxMigrations = {
}
return state;
},
21: (state) => {
// Default the notification types that previously had no push toggle ON for
// existing installs, matching every other type (and 20 above): autoMergeLevel1
// keeps persisted notificationDetails as-is, so a new key is never picked up
// from initialState without a migration.
if (state.application?.notificationDetails) {
const details = state.application.notificationDetails;
details.delegationsNotification = details.delegationsNotification ?? true;
details.payoutsNotification = details.payoutsNotification ?? true;
details.accountUpdateNotification = details.accountUpdateNotification ?? true;
details.weeklyEarningsNotification = details.weeklyEarningsNotification ?? true;
}
return state;
},
};

// Wrap every migration so a throw degrades to "skip this migration" and keep the
Expand Down
Loading