diff --git a/apps/mobile/src/app/(app)/(tabs)/(3_profile)/notification-history.tsx b/apps/mobile/src/app/(app)/(tabs)/(3_profile)/notification-history.tsx
new file mode 100644
index 00000000000..a7a5f7ebbdc
--- /dev/null
+++ b/apps/mobile/src/app/(app)/(tabs)/(3_profile)/notification-history.tsx
@@ -0,0 +1,5 @@
+import { NotificationHistoryScreen } from '@/components/notification-history-screen';
+
+export default function NotificationHistoryRoute() {
+ return ;
+}
diff --git a/apps/mobile/src/components/notification-history-row.tsx b/apps/mobile/src/components/notification-history-row.tsx
new file mode 100644
index 00000000000..b51d68de76b
--- /dev/null
+++ b/apps/mobile/src/components/notification-history-row.tsx
@@ -0,0 +1,45 @@
+import { type inferRouterOutputs, type MobileRouter } from '@kilocode/trpc/mobile';
+import { useTranslation } from 'react-i18next';
+import { Pressable, View } from 'react-native';
+
+import { Text } from '@/components/ui/text';
+import { notificationHistoryCopy } from '@/lib/notification-history';
+import { parseTimestamp, timeAgo } from '@/lib/utils';
+
+type HistoryPage = inferRouterOutputs['user']['getNotificationHistory'];
+export type HistoryRow = HistoryPage['items'][number];
+
+/** One recorded notification: the copy a push showed, and how long ago. */
+export function HistoryRowView({
+ row,
+ now,
+ onOpen,
+}: Readonly<{
+ row: HistoryRow;
+ now: number;
+ onOpen: (payload: Record) => void;
+}>) {
+ const { i18n } = useTranslation();
+ const copy = notificationHistoryCopy(row, i18n.language);
+ return (
+ {
+ onOpen(row.payload);
+ }}
+ >
+
+
+ {copy.title}
+
+
+ {timeAgo(parseTimestamp(row.createdAt), undefined, now)}
+
+
+
+ {copy.body}
+
+
+ );
+}
diff --git a/apps/mobile/src/components/notification-history-screen.mounted.test.tsx b/apps/mobile/src/components/notification-history-screen.mounted.test.tsx
new file mode 100644
index 00000000000..65519b30654
--- /dev/null
+++ b/apps/mobile/src/components/notification-history-screen.mounted.test.tsx
@@ -0,0 +1,454 @@
+/* eslint-disable max-lines -- one mounted harness: the paged list, its states, the chip filter, the row tap and the push banner share one fake tRPC surface */
+import { createElement, type ReactElement } from 'react';
+import { onlineManager } from '@tanstack/react-query';
+import { act, type ReactTestInstance } from '@/test/renderer';
+import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
+
+import { NotificationHistoryScreen } from '@/components/notification-history-screen';
+import { renderWithProviders, waitFor } from '@/test/render-with-providers';
+
+type Row = {
+ id: string;
+ category: string | null;
+ pushType: string;
+ payload: Record;
+ i18nKey: string | null;
+ i18nParams: Record | null;
+ title: string;
+ body: string;
+ createdAt: string;
+};
+
+const HISTORY_PATH = ['user', 'getNotificationHistory'];
+
+const NOW = Date.parse('2026-10-24T10:00:00.000Z');
+
+type PreferencesFixture = { capabilities: Record };
+type GateFixture = {
+ loading: boolean;
+ settled: boolean;
+ permissionGranted: boolean;
+ pushEnabled: boolean;
+};
+
+const state = vi.hoisted(() => {
+ const preferences: PreferencesFixture | undefined = { capabilities: {} };
+ const gate: GateFixture = {
+ loading: false,
+ settled: true,
+ permissionGranted: true,
+ pushEnabled: true,
+ };
+ return {
+ rows: [] as Row[],
+ historyError: undefined as Error | undefined,
+ preferencesError: undefined as Error | undefined,
+ preferences,
+ gate,
+ /** The inputs `infiniteQueryOptions` was built with, newest last. */
+ historyInputs: [] as { category?: string }[],
+ /** The option objects the screen built, newest last. */
+ historyOptions: [] as Record[],
+ /** Rows on page one while a fixture wants a second page. */
+ pageSize: undefined as number | undefined,
+ /** A failure for a non-first page only. */
+ pageError: undefined as Error | undefined,
+ /** The rendered list's paging hook, so a test can drive a next page. */
+ listProps: undefined as { onEndReached?: () => void; onRefresh?: () => void } | undefined,
+ };
+});
+
+const mocks = vi.hoisted(() => ({
+ push: vi.fn(),
+ setPendingDeepLink: vi.fn(),
+ toastError: vi.fn(),
+}));
+
+vi.mock('@shopify/flash-list', () => ({
+ FlashList: ({
+ data,
+ renderItem,
+ onEndReached,
+ onRefresh,
+ ListFooterComponent,
+ }: {
+ data: Row[];
+ renderItem: (info: { item: Row }) => ReactElement;
+ onEndReached?: () => void;
+ onRefresh?: () => void;
+ ListFooterComponent?: ReactElement;
+ }) => {
+ state.listProps = { onEndReached, onRefresh };
+ return createElement(
+ 'FlashList',
+ null,
+ data.map(item => createElement('Cell', { key: item.id }, renderItem({ item }))),
+ ListFooterComponent ?? null
+ );
+ },
+}));
+
+vi.mock('react-native', () => ({
+ View: 'View',
+ Pressable: 'Pressable',
+ ScrollView: 'ScrollView',
+}));
+
+vi.mock('expo-router', () => ({ useRouter: () => ({ push: mocks.push }) }));
+vi.mock('sonner-native', () => ({ toast: { error: mocks.toastError } }));
+vi.mock('@/components/ui/icons', () => ({ BellOff: 'BellOff' }));
+vi.mock('@/components/screen-header', () => ({ ScreenHeader: 'ScreenHeader' }));
+vi.mock('@/components/empty-state', () => ({ EmptyState: 'EmptyState' }));
+vi.mock('@/components/query-error', () => ({ QueryError: 'QueryError' }));
+vi.mock('@/components/ui/button', () => ({ Button: 'Button' }));
+vi.mock('@/components/ui/refresh-control', () => ({ RefreshControl: 'RefreshControl' }));
+vi.mock('@/components/ui/skeleton', () => ({ Skeleton: 'Skeleton' }));
+vi.mock('@/components/ui/text', () => ({ Text: 'Text' }));
+vi.mock('@/components/tab-screen', () => ({ useTabBarBottomPadding: () => 24 }));
+vi.mock('@/lib/auth/auth-context', () => ({ useAuth: () => ({ token: 'jwt' }) }));
+vi.mock('@/lib/hooks/use-now-ticker', () => ({ useNowTicker: () => NOW }));
+vi.mock('@/lib/hooks/use-notification-master-gate', () => ({
+ useNotificationMasterGate: () => state.gate,
+}));
+vi.mock('@/lib/hooks/use-theme-colors', () => ({
+ useThemeColors: () => ({ mutedForeground: '#71717a' }),
+}));
+vi.mock('@/lib/deep-link-launch', () => ({
+ setPendingDeepLink: mocks.setPendingDeepLink,
+}));
+vi.mock('@/lib/notification-actions', () => ({
+ pendingDeepLinkOptionsForData: () => ({ sessionBound: false }),
+}));
+vi.mock('@/lib/trpc', () => ({
+ useTRPC: () => ({
+ user: {
+ getNotificationHistory: {
+ // tRPC's real shapes: the path is the first element, and only the path
+ // prefix matches every variant of the read.
+ pathKey: () => [HISTORY_PATH],
+ infiniteQueryKey: (input: { category?: string }) => [
+ HISTORY_PATH,
+ { input, type: 'infinite' },
+ ],
+ infiniteQueryOptions: (input: { category?: string }, options: Record) => {
+ state.historyInputs.push(input);
+ state.historyOptions.push(options);
+ return {
+ queryKey: [HISTORY_PATH, { input, type: 'infinite' }],
+ initialPageParam: undefined,
+ queryFn: ({ pageParam }: { pageParam: string | undefined }) => {
+ if (pageParam !== undefined && state.pageError) {
+ throw state.pageError;
+ }
+ if (state.historyError) {
+ throw state.historyError;
+ }
+ const items = state.rows.filter(
+ entry => input.category === undefined || entry.category === input.category
+ );
+ if (state.pageSize === undefined) {
+ return { items, nextCursor: null };
+ }
+ if (pageParam === undefined) {
+ return {
+ items: items.slice(0, state.pageSize),
+ nextCursor: items.length > state.pageSize ? 'page-2' : null,
+ };
+ }
+ return { items: items.slice(state.pageSize), nextCursor: null };
+ },
+ ...options,
+ };
+ },
+ },
+ getNotificationPreferences: {
+ queryOptions: () => ({
+ queryKey: ['user', 'getNotificationPreferences'],
+ queryFn: () => {
+ if (state.preferencesError) {
+ throw state.preferencesError;
+ }
+ return state.preferences;
+ },
+ }),
+ },
+ },
+ }),
+}));
+
+function row(overrides: Partial = {}): Row {
+ return {
+ id: 'row-1',
+ category: 'balanceAlerts',
+ pushType: 'low_balance',
+ payload: { type: 'low_balance', organizationId: 'org-acme' },
+ i18nKey: 'internal.lowBalance',
+ i18nParams: { organizationName: 'Acme', minimumBalanceUsd: '5' },
+ title: 'stored title',
+ body: 'stored body',
+ createdAt: new Date(NOW - 5 * 60_000).toISOString(),
+ ...overrides,
+ };
+}
+
+let view: Awaited> | undefined = undefined;
+
+async function mount(): Promise>> {
+ view = await renderWithProviders();
+ await waitFor(() => view?.renderer.root.findAllByType('Skeleton').length === 0);
+ return view;
+}
+
+function byType(type: string) {
+ const root = view?.renderer.root;
+ if (!root) {
+ throw new Error('screen not mounted');
+ }
+ return root.findAll(node => node.type === type);
+}
+
+/** Press a control; a missing control fails the test instead of a no-op. */
+function press(node: ReactTestInstance | undefined) {
+ if (!node) {
+ throw new Error('control not mounted');
+ }
+ act(() => {
+ (node.props.onPress as () => void)();
+ });
+}
+
+/** The screen's button props, read once so the chip assertions stay typed. */
+type ChipProps = { role?: string; accessibilityState?: { selected?: boolean } };
+
+function chipProps(node: ReactTestInstance | undefined): ChipProps | undefined {
+ return node?.props as ChipProps | undefined;
+}
+
+/** The footer's bottom padding, read once so the clearance assertion stays typed. */
+function footerPadding(node: ReactTestInstance): number | undefined {
+ return (node.props as { style?: { paddingBottom?: number } }).style?.paddingBottom;
+}
+
+/** The node whose `Text` child holds `value`. */
+function withText(nodes: ReactTestInstance[], value: string) {
+ return nodes.find(
+ node =>
+ node.findAll(
+ child =>
+ typeof child.type === 'string' &&
+ (child.type as string) === 'Text' &&
+ child.props.children === value
+ ).length > 0
+ );
+}
+
+beforeEach(() => {
+ vi.stubGlobal('IS_REACT_ACT_ENVIRONMENT', true);
+ vi.stubGlobal('__DEV__', false);
+ state.rows = [];
+ state.historyError = undefined;
+ state.preferencesError = undefined;
+ state.preferences = { capabilities: {} };
+ state.gate = { loading: false, settled: true, permissionGranted: true, pushEnabled: true };
+ state.historyInputs = [];
+ state.historyOptions = [];
+ state.pageSize = undefined;
+ state.pageError = undefined;
+ state.listProps = undefined;
+ vi.clearAllMocks();
+});
+
+afterEach(() => {
+ onlineManager.setOnline(true);
+ view?.unmount();
+ view = undefined;
+ vi.unstubAllGlobals();
+});
+
+describe('NotificationHistoryScreen', () => {
+ it('renders each row from the stored catalog key in the app language', async () => {
+ state.rows = [row()];
+ await mount();
+
+ const texts = byType('Text').map(node => node.props.children);
+ expect(texts).toContain('Low balance alert');
+ expect(texts).toContain('Acme balance fell below $5');
+ expect(texts).toContain('5 minutes ago');
+ expect(texts).not.toContain('stored title');
+ });
+
+ it('filters the list by the tapped chip', async () => {
+ state.rows = [row(), row({ id: 'row-2', category: 'chatMessages', pushType: 'chat.message' })];
+ await mount();
+ expect(byType('Cell')).toHaveLength(2);
+
+ const chatChip = withText(byType('Button'), 'Chat messages');
+ press(chatChip);
+
+ await waitFor(() => byType('Cell').length === 1);
+ expect(state.historyInputs.at(-1)).toMatchObject({ category: 'chatMessages' });
+ });
+
+ it('opens a row through the pending deep link slot, not the router', async () => {
+ state.rows = [row()];
+ await mount();
+
+ const pressable = byType('Pressable')[0];
+ press(pressable);
+
+ expect(mocks.setPendingDeepLink).toHaveBeenCalledWith(
+ '/(app)/(tabs)/(3_profile)/organization/credit-activity?org=org-acme',
+ 'notification',
+ { sessionBound: false }
+ );
+ expect(mocks.push).not.toHaveBeenCalled();
+ });
+
+ it('keeps the list and reports a row whose stored payload no longer resolves', async () => {
+ state.rows = [row({ payload: { type: 'retired_kind' } })];
+ await mount();
+
+ const pressable = byType('Pressable')[0];
+ press(pressable);
+
+ expect(mocks.toastError).toHaveBeenCalledWith('This notification no longer opens anywhere.');
+ expect(mocks.setPendingDeepLink).not.toHaveBeenCalled();
+ expect(byType('Cell')).toHaveLength(1);
+ });
+
+ it('states the retention window when the account has no rows', async () => {
+ await mount();
+
+ const empty = byType('EmptyState')[0];
+ expect(empty?.props).toMatchObject({
+ title: 'No notifications yet',
+ description: 'Notifications this account receives appear here. They are kept for 90 days.',
+ });
+ });
+
+ it('offers a retry when the first page fails', async () => {
+ state.historyError = new Error('offline');
+ await mount();
+
+ const failure = byType('QueryError')[0];
+ expect(failure?.props).toMatchObject({ message: 'Could not load notification history.' });
+ expect(typeof failure?.props.onRetry).toBe('function');
+ });
+
+ it('shows the settings banner while the push gate is closed and still loads the list', async () => {
+ state.gate = { loading: false, settled: true, permissionGranted: false, pushEnabled: false };
+ state.rows = [row()];
+ await mount();
+
+ const banner = withText(byType('Button'), 'Notification settings');
+ expect(banner).toBeDefined();
+ expect(byType('Cell')).toHaveLength(1);
+
+ press(banner);
+ expect(mocks.push).toHaveBeenCalledWith('/(app)/notifications');
+ });
+
+ it('shows no banner while push reaches the device', async () => {
+ state.rows = [row()];
+ await mount();
+
+ const texts = byType('Text').map(node => node.props.children);
+ expect(texts).not.toContain('Notification settings');
+ });
+
+ it('announces the chip row as a tab list with the selected chip marked', async () => {
+ state.rows = [row({ category: 'chatMessages', pushType: 'chat.message' })];
+ await mount();
+
+ const chip = withText(byType('Button'), 'Chat messages');
+ expect(chipProps(chip)).toMatchObject({
+ role: 'tab',
+ accessibilityState: { selected: false },
+ });
+
+ press(chip);
+ await waitFor(
+ () =>
+ chipProps(withText(byType('Button'), 'Chat messages'))?.accessibilityState?.selected ===
+ true
+ );
+ });
+
+ it('says a category has nothing rather than that the account has nothing', async () => {
+ state.rows = [row({ category: 'balanceAlerts' })];
+ await mount();
+ press(withText(byType('Button'), 'Chat messages'));
+ await waitFor(() => byType('EmptyState').length === 1);
+
+ const empty = byType('EmptyState')[0];
+ expect(empty?.props).toMatchObject({ title: 'No notifications in this category' });
+ // The retention window describes the whole list, not one empty category.
+ expect(empty?.props.description).toBeUndefined();
+ });
+
+ it('collapses retained pages on a refresh instead of re-reading their cursors', async () => {
+ state.pageSize = 3;
+ state.rows = [1, 2, 3, 4, 5].map(index =>
+ row({ id: `row-${index}`, title: `stored ${index}` })
+ );
+ await mount();
+ expect(byType('Cell')).toHaveLength(3);
+
+ act(() => {
+ state.listProps?.onEndReached?.();
+ });
+ await waitFor(() => byType('Cell').length === 5);
+
+ act(() => {
+ state.listProps?.onRefresh?.();
+ });
+ await waitFor(() => byType('Cell').length === 3);
+ });
+
+ it('reports a refresh that fails instead of leaving the list silent', async () => {
+ state.rows = [row()];
+ await mount();
+
+ state.historyError = new Error('offline');
+ act(() => {
+ state.listProps?.onRefresh?.();
+ });
+
+ await waitFor(() => mocks.toastError.mock.calls.length > 0);
+ expect(mocks.toastError).toHaveBeenCalledWith("Couldn't refresh. Pull down to try again.");
+ });
+
+ it('keeps the tab-bar clearance under the next-page retry', async () => {
+ state.pageSize = 1;
+ state.rows = [row({ id: 'row-1' }), row({ id: 'row-2' })];
+ await mount();
+
+ state.pageError = new Error('paging failed');
+ act(() => {
+ state.listProps?.onEndReached?.();
+ });
+ await waitFor(() => withText(byType('Button'), 'Retry loading more') !== undefined);
+
+ // 24dp of tab bar plus the footer's own 32dp.
+ const footer = byType('View').find(node => footerPadding(node) === 56);
+ expect(footer).toBeDefined();
+ });
+
+ it('settles the chip row offline instead of holding its skeletons', async () => {
+ // The online manager reports offline, so a read that keeps the default
+ // `networkMode: 'online'` pauses in `pending` and never renders a chip.
+ onlineManager.setOnline(false);
+ state.preferencesError = new Error('offline');
+ await mount();
+
+ expect(withText(byType('Button'), 'All')).toBeDefined();
+ expect(withText(byType('Button'), 'Chat messages')).toBeDefined();
+ });
+
+ it('turns off the network gate so an offline first load can fail into its retry', async () => {
+ state.rows = [row()];
+ await mount();
+
+ expect(state.historyOptions.at(-1)).toMatchObject({ networkMode: 'always' });
+ });
+});
diff --git a/apps/mobile/src/components/notification-history-screen.tsx b/apps/mobile/src/components/notification-history-screen.tsx
new file mode 100644
index 00000000000..8b4579be64e
--- /dev/null
+++ b/apps/mobile/src/components/notification-history-screen.tsx
@@ -0,0 +1,348 @@
+import { FlashList } from '@shopify/flash-list';
+import { useInfiniteQuery, useQuery, useQueryClient } from '@tanstack/react-query';
+import { type Href, useRouter } from 'expo-router';
+import { useCallback, useMemo, useState } from 'react';
+import { useTranslation } from 'react-i18next';
+import { ScrollView, View, type ViewStyle } from 'react-native';
+import { toast } from 'sonner-native';
+
+import { NOTIFICATION_HISTORY_RETENTION_DAYS } from '@kilocode/notifications';
+
+import { EmptyState } from '@/components/empty-state';
+import { QueryError } from '@/components/query-error';
+import { ScreenHeader } from '@/components/screen-header';
+import { RefreshControl } from '@/components/ui/refresh-control';
+import { BellOff } from '@/components/ui/icons';
+import { Button } from '@/components/ui/button';
+import { Skeleton } from '@/components/ui/skeleton';
+import { Text } from '@/components/ui/text';
+import { useAuth } from '@/lib/auth/auth-context';
+import { setPendingDeepLink } from '@/lib/deep-link-launch';
+import { pendingDeepLinkOptionsForData } from '@/lib/notification-actions';
+import {
+ NOTIFICATION_HISTORY_FILTERS,
+ type NotificationHistoryFilter,
+ notificationHistoryFilters,
+ notificationHistoryTarget,
+} from '@/lib/notification-history';
+import { useNotificationMasterGate } from '@/lib/hooks/use-notification-master-gate';
+import { type HistoryRow, HistoryRowView } from '@/components/notification-history-row';
+import { useNowTicker } from '@/lib/hooks/use-now-ticker';
+import { useTabBarBottomPadding } from '@/components/tab-screen';
+import { useThemeColors } from '@/lib/hooks/use-theme-colors';
+import {
+ hasRetentionBudget,
+ reconcileFirstPage,
+ withInfiniteRetention,
+} from '@/lib/query/infinite-retention';
+import { useTRPC } from '@/lib/trpc';
+
+/** Rows requested per page; the server defaults to the same 30. */
+const PAGE_SIZE = 30;
+/** How often a row's relative time is re-read while the screen stays open. */
+const RELATIVE_TIME_TICK_MS = 60_000;
+
+/** Retained pages; the bound refuses the next page rather than dropping the newest. */
+const MAX_PAGES = 10;
+
+const listStyle = { flex: 1 } satisfies ViewStyle;
+const listContentContainerStyle = { paddingHorizontal: 16 } satisfies ViewStyle;
+
+/** One filter chip. Single-select, so the active one carries `selected`. */
+function FilterChip({
+ label,
+ active,
+ onPress,
+}: Readonly<{ label: string; active: boolean; onPress: () => void }>) {
+ return (
+
+ );
+}
+
+/**
+ * The pushes this account received, newest first: one row per recorded
+ * notification, rendered on the device from the catalog key and params the push
+ * carried, filtered by the same categories the settings screen toggles, and
+ * opening the destination a push tap opens.
+ *
+ * The list is independent of the push gate: a user whose pushes are off sees
+ * what arrived before they were, with a banner that leads to the switch.
+ */
+export function NotificationHistoryScreen() {
+ const trpc = useTRPC();
+ const router = useRouter();
+ const colors = useThemeColors();
+ const { t } = useTranslation();
+ const { token: authToken } = useAuth();
+ const gate = useNotificationMasterGate();
+ const now = useNowTicker(RELATIVE_TIME_TICK_MS);
+ const queryClient = useQueryClient();
+ const paddingBottom = useTabBarBottomPadding();
+ const [category, setCategory] = useState(null);
+ const [refreshing, setRefreshing] = useState(false);
+
+ const preferences = useQuery({
+ ...trpc.user.getNotificationPreferences.queryOptions(),
+ enabled: authToken != null,
+ // Same reason as the list: a read started offline must settle into its
+ // answer or its error instead of pausing in `pending`, which would hold the
+ // chip skeletons on screen for as long as the device stays offline. A
+ // settled failure falls back to every chip.
+ networkMode: 'always',
+ // `networkMode: 'always'` also drops the derived reconnect refetch.
+ refetchOnReconnect: true,
+ });
+
+ const query = useInfiniteQuery(
+ withInfiniteRetention(
+ trpc.user.getNotificationHistory.infiniteQueryOptions(
+ { category: category ?? undefined, limit: PAGE_SIZE },
+ {
+ enabled: authToken != null,
+ // A newest-first list reaches its retention bound by refusing the
+ // next page: `maxPages` alone would drop index 0, which is the newest
+ // page.
+ getNextPageParam: (lastPage, pages) =>
+ hasRetentionBudget(pages.length, MAX_PAGES)
+ ? (lastPage.nextCursor ?? undefined)
+ : undefined,
+ // A first load while offline must settle into the error state and its
+ // retry rather than pausing in `pending`, which would leave the
+ // skeleton on screen for as long as the device stays offline.
+ networkMode: 'always',
+ // `networkMode: 'always'` also drops the derived reconnect refetch,
+ // so ask for it back.
+ refetchOnReconnect: true,
+ }
+ ),
+ MAX_PAGES
+ )
+ );
+
+ const flatItems = useMemo(
+ () => query.data?.pages.flatMap(page => page.items) ?? [],
+ [query.data?.pages]
+ );
+ // `reconcileFirstPage` empties the cached pages before page one refetches, so
+ // the rows a refresh started from stay on screen until it delivers. Without
+ // the hold the list would flash its empty state and lose its scroll.
+ const [refreshHold, setRefreshHold] = useState(null);
+ const items = flatItems.length > 0 ? flatItems : (refreshHold ?? []);
+ const hasListContent = items.length > 0 || query.isFetchNextPageError;
+ // A query that is disabled never leaves `pending`; only a signed-in read can.
+ const loading = authToken != null && query.isPending && !query.isError;
+ const failure = !loading && query.isError && !hasListContent;
+
+ const openRow = useCallback(
+ (payload: Record) => {
+ const target = notificationHistoryTarget(payload);
+ if (target === null) {
+ toast.error(t('notifications.history.openFailed'));
+ return;
+ }
+ // The same slot a notification response fills: the root layout navigates.
+ setPendingDeepLink(target.href, 'notification', pendingDeepLinkOptionsForData(target.data));
+ },
+ [t]
+ );
+
+ const renderItem = useCallback(
+ ({ item }: { item: HistoryRow }) => ,
+ [now, openRow]
+ );
+
+ const handleRefresh = () => {
+ void (async () => {
+ setRefreshing(true);
+ setRefreshHold(items);
+ try {
+ // A retained page is a keyset window that only stays contiguous with
+ // the page above it while page one's newest row is unchanged. Rows that
+ // arrived since would move page one's tail past the older pages'
+ // cursors, so a refresh collapses the list and reloads from the newest
+ // row instead of re-requesting every retained window.
+ //
+ // Three different keys: the path resets every variant of this read, the
+ // infinite key is the entry the list observes, and `getQueryState` only
+ // answers an exact key.
+ const path = trpc.user.getNotificationHistory.pathKey();
+ const key = trpc.user.getNotificationHistory.infiniteQueryKey({
+ category: category ?? undefined,
+ limit: PAGE_SIZE,
+ });
+ reconcileFirstPage(queryClient, path);
+ await queryClient.refetchQueries(
+ { queryKey: key, type: 'active' },
+ // `cancelRefetch: false` awaits the page-one fetch the invalidate
+ // started instead of cancelling it and issuing a second one.
+ { cancelRefetch: false }
+ );
+ if (queryClient.getQueryState(key)?.status === 'error') {
+ toast.error(t('common.couldNotRefresh'));
+ }
+ } finally {
+ setRefreshing(false);
+ setRefreshHold(null);
+ }
+ })();
+ };
+ const refreshControl = ;
+
+ const filters = preferences.data
+ ? notificationHistoryFilters(preferences.data.capabilities)
+ : NOTIFICATION_HISTORY_FILTERS;
+
+ // The page bound, not the server, ends the list: say so.
+ const capped = !query.hasNextPage && (query.data?.pages.at(-1)?.nextCursor ?? null) !== null;
+ let footer = (
+
+ {capped ? (
+
+ {t('notifications.history.capped')}
+
+ ) : null}
+
+ );
+ if (query.isFetchNextPageError) {
+ footer = (
+
+
+
+ );
+ } else if (query.isFetchingNextPage) {
+ footer = ;
+ }
+
+ return (
+
+
+
+ {gate.settled && !gate.pushEnabled ? (
+
+
+
+ {t('notifications.history.bannerBody')}
+
+
+
+ ) : null}
+
+ {preferences.isPending && authToken != null ? (
+
+
+
+
+
+ ) : (
+
+ {filters.map(filter => (
+ {
+ setCategory(filter.category);
+ }}
+ />
+ ))}
+
+ )}
+
+
+ {loading ? (
+
+
+
+
+
+ ) : null}
+
+ {failure ? (
+ {
+ void query.refetch();
+ }}
+ isRetrying={query.isFetching}
+ refreshControl={refreshControl}
+ />
+ ) : null}
+
+ {!loading && !failure && !hasListContent ? (
+
+ ) : null}
+
+ {!loading && !failure && hasListContent ? (
+
+ style={listStyle}
+ data={items}
+ keyExtractor={item => item.id}
+ renderItem={renderItem}
+ contentContainerStyle={listContentContainerStyle}
+ // FlashList adds its own control from this pair; the element
+ // below is for the state scrollers that accept one.
+ refreshing={refreshing}
+ onRefresh={handleRefresh}
+ onEndReached={() => {
+ // Once the next page has failed, auto-loading re-fires on every
+ // content-size change, so recovery waits for the footer's tap.
+ if (query.hasNextPage && !query.isFetchingNextPage && !query.isFetchNextPageError) {
+ void query.fetchNextPage();
+ }
+ }}
+ onEndReachedThreshold={0.5}
+ ListFooterComponent={footer}
+ />
+ ) : null}
+
+
+
+ );
+}
diff --git a/apps/mobile/src/components/preferences-screen.mounted.test.tsx b/apps/mobile/src/components/preferences-screen.mounted.test.tsx
index 1e384568e0f..87cf66a1ccd 100644
--- a/apps/mobile/src/components/preferences-screen.mounted.test.tsx
+++ b/apps/mobile/src/components/preferences-screen.mounted.test.tsx
@@ -18,6 +18,7 @@ vi.mock('expo-router', () => ({
vi.mock('@/components/ui/icons', () => ({
Bell: 'Bell',
Globe: 'Globe',
+ History: 'History',
Mic: 'Mic',
SlidersHorizontal: 'SlidersHorizontal',
Wallet: 'Wallet',
@@ -68,7 +69,7 @@ describe('PreferencesScreen hub', () => {
it('renders one navigation row per settings group with its title and subtitle', async () => {
const renderer = await mountPreferences();
- expect(hubRows(renderer)).toHaveLength(6);
+ expect(hubRows(renderer)).toHaveLength(7);
expect(row(renderer, 'General').props).toMatchObject({ icon: 'SlidersHorizontal', last: true });
expect(row(renderer, 'Voice input').props).toMatchObject({
icon: 'Mic',
@@ -92,6 +93,11 @@ describe('PreferencesScreen hub', () => {
subtitle: 'Push preferences',
last: true,
});
+ expect(row(renderer, i18n.t('notifications.history.title')).props).toMatchObject({
+ icon: 'History',
+ subtitle: 'Notifications this account received',
+ last: true,
+ });
expect(row(renderer, i18n.t('notifications.channel.spend')).props).toMatchObject({
icon: 'Wallet',
last: true,
@@ -104,6 +110,7 @@ describe('PreferencesScreen hub', () => {
['Translate tool summaries', '/(app)/(tabs)/(3_profile)/tool-summary-translation'],
['Account', '/(app)/(tabs)/(3_profile)/account'],
['Notifications', '/(app)/notifications'],
+ [i18n.t('notifications.history.title'), '/(app)/(tabs)/(3_profile)/notification-history'],
[i18n.t('notifications.channel.spend'), '/(app)/(tabs)/(3_profile)/spend-alerts'],
])('pushes the %s subpage from its row', async (title, route) => {
const renderer = await mountPreferences();
diff --git a/apps/mobile/src/components/preferences-screen.tsx b/apps/mobile/src/components/preferences-screen.tsx
index 5a3dc5196f5..ce8316fde31 100644
--- a/apps/mobile/src/components/preferences-screen.tsx
+++ b/apps/mobile/src/components/preferences-screen.tsx
@@ -1,5 +1,13 @@
import { type Href, useRouter } from 'expo-router';
-import { Bell, Globe, Mic, SlidersHorizontal, Wallet, WandSparkles } from '@/components/ui/icons';
+import {
+ Bell,
+ Globe,
+ History,
+ Mic,
+ SlidersHorizontal,
+ Wallet,
+ WandSparkles,
+} from '@/components/ui/icons';
import { View } from 'react-native';
import { useTranslation } from 'react-i18next';
@@ -99,6 +107,16 @@ export function PreferencesScreen() {
router.push('/(app)/notifications' as Href);
}}
/>
+ {
+ router.push('/(app)/(tabs)/(3_profile)/notification-history' as Href);
+ }}
+ />
({
vi.mock('@/components/ui/icons', () => ({
Bell: 'Bell',
Globe: 'Globe',
+ History: 'History',
KeyRound: 'KeyRound',
Mic: 'Mic',
Shield: 'Shield',
diff --git a/apps/mobile/src/i18n/locales/af.json b/apps/mobile/src/i18n/locales/af.json
index a6e49cd6635..d8458045bbb 100644
--- a/apps/mobile/src/i18n/locales/af.json
+++ b/apps/mobile/src/i18n/locales/af.json
@@ -877,6 +877,18 @@
"spendAlertsSubtitle": "oorskryding van drempels en uurlikse pieke",
"spendAlertsToggle": "Kategorie vir bestedingswaarskuwings"
},
+ "history": {
+ "title": "Kennisgewingsgeskiedenis",
+ "subtitle": "Kennisgewings wat hierdie rekening ontvang het",
+ "bannerBody": "Push-kennisgewings is af vir hierdie toestel, so nuwe kennisgewings sal nie hier aankom nie.",
+ "bannerAction": "Kennisgewingsinstellings",
+ "emptyTitle": "Nog geen kennisgewings nie",
+ "emptyCategoryTitle": "Geen kennisgewings in hierdie kategorie nie",
+ "emptyBody": "Kennisgewings wat hierdie rekening ontvang, verskyn hier. Hulle word vir {{days}} dae bewaar.",
+ "capped": "Die mees onlangse kennisgewings word gewys.",
+ "loadError": "Kon nie kennisgewingsgeskiedenis laai nie.",
+ "openFailed": "Hierdie kennisgewing open nêrens meer nie."
+ },
"action": {
"openSession": "Maak sessie oop"
},
diff --git a/apps/mobile/src/i18n/locales/am.json b/apps/mobile/src/i18n/locales/am.json
index 6a0d9710472..62ba1c6ffdf 100644
--- a/apps/mobile/src/i18n/locales/am.json
+++ b/apps/mobile/src/i18n/locales/am.json
@@ -877,6 +877,18 @@
"spendAlertsSubtitle": "የገደብ መሻገሪያዎች እና የሰዓት ጭማሪዎች",
"spendAlertsToggle": "የወጪ ማስጠንቀቂያ ምድብ"
},
+ "history": {
+ "title": "የማሳወቂያ ታሪክ",
+ "subtitle": "ይህ መለያ የተቀበላቸው ማሳወቂያዎች",
+ "bannerBody": "ለዚህ መሣሪያ ግፋ ማሳወቂያዎች ጠፍተዋል፣ ስለዚህ አዲሶች እዚህ አይደርሱም።",
+ "bannerAction": "የማሳወቂያ ቅንብሮች",
+ "emptyTitle": "እስካሁን ማሳወቂያዎች የሉም",
+ "emptyCategoryTitle": "በዚህ ምድብ ውስጥ ማሳወቂያዎች የሉም",
+ "emptyBody": "ይህ መለያ የሚቀበላቸው ማሳወቂያዎች እዚህ ይታያሉ። ለ{{days}} ቀናት ይቀመጣሉ።",
+ "capped": "የቅርብ ጊዜ ማሳወቂያዎችን በማሳየት ላይ።",
+ "loadError": "የማሳወቂያ ታሪክን መጫን አልተቻለም።",
+ "openFailed": "ይህ ማሳወቂያ ከእንግዲህ በየትኛውም ቦታ አይከፈትም።"
+ },
"action": {
"openSession": "ክፍለ ጊዜውን ክፈት"
},
diff --git a/apps/mobile/src/i18n/locales/ar.json b/apps/mobile/src/i18n/locales/ar.json
index d2c40d5addf..fb071d5c695 100644
--- a/apps/mobile/src/i18n/locales/ar.json
+++ b/apps/mobile/src/i18n/locales/ar.json
@@ -353,6 +353,18 @@
"spendAlertsSubtitle": "تجاوزات الحدود والارتفاعات الساعية",
"spendAlertsToggle": "فئة تنبيهات الإنفاق"
},
+ "history": {
+ "title": "سجل الإشعارات",
+ "subtitle": "الإشعارات التي تلقّاها هذا الحساب",
+ "bannerBody": "الإشعارات الفورية معطّلة على هذا الجهاز، لذلك لن تصل الإشعارات الجديدة هنا.",
+ "bannerAction": "إعدادات الإشعارات",
+ "emptyTitle": "لا توجد إشعارات بعد",
+ "emptyCategoryTitle": "لا توجد إشعارات في هذا التصنيف",
+ "emptyBody": "تظهر هنا الإشعارات التي يتلقّاها هذا الحساب. يتم الاحتفاظ بها لمدة {{days}} يوم.",
+ "capped": "يتم عرض أحدث الإشعارات.",
+ "loadError": "تعذّر تحميل سجل الإشعارات.",
+ "openFailed": "لم يعد هذا الإشعار يفتح في أي مكان."
+ },
"action": {
"openSession": "فتح الجلسة"
},
diff --git a/apps/mobile/src/i18n/locales/az.json b/apps/mobile/src/i18n/locales/az.json
index d34ec75024d..929ec6bc8fc 100644
--- a/apps/mobile/src/i18n/locales/az.json
+++ b/apps/mobile/src/i18n/locales/az.json
@@ -877,6 +877,18 @@
"spendAlertsSubtitle": "hədd aşmaları və saatlıq sıçrayışlar",
"spendAlertsToggle": "Xərc bildirişləri kateqoriyası"
},
+ "history": {
+ "title": "Bildiriş tarixçəsi",
+ "subtitle": "Bu hesabın aldığı bildirişlər",
+ "bannerBody": "Bu cihaz üçün push bildirişlər deaktivdir, ona görə də yeni bildirişlər bura gəlməyəcək.",
+ "bannerAction": "Bildiriş parametrləri",
+ "emptyTitle": "Hələ bildiriş yoxdur",
+ "emptyCategoryTitle": "Bu kateqoriyada bildiriş yoxdur",
+ "emptyBody": "Bu hesabın aldığı bildirişlər burada görünür. Onlar {{days}} gün saxlanılır.",
+ "capped": "Ən son bildirişlər göstərilir.",
+ "loadError": "Bildiriş tarixçəsini yükləmək mümkün olmadı.",
+ "openFailed": "Bu bildiriş artıq heç yerdə açılmır."
+ },
"action": {
"openSession": "Sessiyanı aç"
},
diff --git a/apps/mobile/src/i18n/locales/be.json b/apps/mobile/src/i18n/locales/be.json
index d3fbe369cf1..e45eb05855c 100644
--- a/apps/mobile/src/i18n/locales/be.json
+++ b/apps/mobile/src/i18n/locales/be.json
@@ -915,6 +915,18 @@
"spendAlertsSubtitle": "перасячэнні парогаў і гадзінныя ўсплёскі",
"spendAlertsToggle": "Катэгорыя апавяшчэнняў аб выдатаках"
},
+ "history": {
+ "title": "Гісторыя апавяшчэнняў",
+ "subtitle": "Апавяшчэнні, атрыманыя гэтым акаўнтам",
+ "bannerBody": "Push-апавяшчэнні адключаны для гэтай прылады, таму новыя сюды не паступаюць.",
+ "bannerAction": "Налады апавяшчэнняў",
+ "emptyTitle": "Пакуль няма апавяшчэнняў",
+ "emptyCategoryTitle": "У гэтай катэгорыі няма апавяшчэнняў",
+ "emptyBody": "Апавяшчэнні, якія атрымлівае гэты акаўнт, з'яўляюцца тут. Яны захоўваюцца {{days}} дзён.",
+ "capped": "Паказваюцца самыя апошнія апавяшчэнні.",
+ "loadError": "Не ўдалося загрузіць гісторыю апавяшчэнняў.",
+ "openFailed": "Гэтае апавяшчэнне больш нідзе не адкрываецца."
+ },
"action": {
"openSession": "Адкрыць сеанс"
},
diff --git a/apps/mobile/src/i18n/locales/bg.json b/apps/mobile/src/i18n/locales/bg.json
index 2dcf7bc897c..53e68805a5c 100644
--- a/apps/mobile/src/i18n/locales/bg.json
+++ b/apps/mobile/src/i18n/locales/bg.json
@@ -877,6 +877,18 @@
"spendAlertsSubtitle": "преминаване на прагове и часови скокове",
"spendAlertsToggle": "Категория известия за разходи"
},
+ "history": {
+ "title": "История на известията",
+ "subtitle": "Известия, получени от този акаунт",
+ "bannerBody": "Push известията са изключени за това устройство, така че нови няма да пристигат тук.",
+ "bannerAction": "Настройки на известията",
+ "emptyTitle": "Все още няма известия",
+ "emptyCategoryTitle": "Няма известия в тази категория",
+ "emptyBody": "Известията, които този акаунт получава, се показват тук. Те се запазват за {{days}} дни.",
+ "capped": "Показват се най-новите известия.",
+ "loadError": "Не можахме да заредим историята на известията.",
+ "openFailed": "Това известие вече не се отваря никъде."
+ },
"action": {
"openSession": "Отваряне на сесия"
},
diff --git a/apps/mobile/src/i18n/locales/bn.json b/apps/mobile/src/i18n/locales/bn.json
index 7a86f2c3f0f..450bbe523a1 100644
--- a/apps/mobile/src/i18n/locales/bn.json
+++ b/apps/mobile/src/i18n/locales/bn.json
@@ -877,6 +877,18 @@
"spendAlertsSubtitle": "সীমা অতিক্রম ও ঘণ্টাভিত্তিক বৃদ্ধি",
"spendAlertsToggle": "খরচের সতর্কতার বিভাগ"
},
+ "history": {
+ "title": "বিজ্ঞপ্তির ইতিহাস",
+ "subtitle": "এই অ্যাকাউন্টে প্রাপ্ত বিজ্ঞপ্তি",
+ "bannerBody": "এই ডিভাইসে পুশ বিজ্ঞপ্তি বন্ধ আছে, তাই নতুন বিজ্ঞপ্তি এখানে আসবে না।",
+ "bannerAction": "বিজ্ঞপ্তি সেটিংস",
+ "emptyTitle": "এখনো কোনো বিজ্ঞপ্তি নেই",
+ "emptyCategoryTitle": "এই বিভাগে কোনো বিজ্ঞপ্তি নেই",
+ "emptyBody": "এই অ্যাকাউন্টে প্রাপ্ত বিজ্ঞপ্তি এখানে দেখা যাবে। সেগুলো {{days}} দিন রাখা হয়।",
+ "capped": "সাম্প্রতিকতম বিজ্ঞপ্তিগুলো দেখানো হচ্ছে।",
+ "loadError": "বিজ্ঞপ্তির ইতিহাস লোড করা যায়নি।",
+ "openFailed": "এই বিজ্ঞপ্তিটি আর কোথাও খোলে না।"
+ },
"action": {
"openSession": "সেশন খুলুন"
},
diff --git a/apps/mobile/src/i18n/locales/bs.json b/apps/mobile/src/i18n/locales/bs.json
index 20d3b2081b9..d03e7dee367 100644
--- a/apps/mobile/src/i18n/locales/bs.json
+++ b/apps/mobile/src/i18n/locales/bs.json
@@ -896,6 +896,18 @@
"spendAlertsSubtitle": "prekoračenja pragova i skokovi na sat",
"spendAlertsToggle": "Kategorija upozorenja o potrošnji"
},
+ "history": {
+ "title": "Historija obavijesti",
+ "subtitle": "Obavijesti koje je ovaj račun primio",
+ "bannerBody": "Push obavijesti su isključene za ovaj uređaj, pa nove neće stizati ovdje.",
+ "bannerAction": "Postavke obavijesti",
+ "emptyTitle": "Još nema obavijesti",
+ "emptyCategoryTitle": "Nema obavijesti u ovoj kategoriji",
+ "emptyBody": "Obavijesti koje ovaj račun primi pojavljuju se ovdje. Čuvaju se {{days}} dana.",
+ "capped": "Prikazuju se najnovije obavijesti.",
+ "loadError": "Nije moguće učitati historiju obavijesti.",
+ "openFailed": "Ova obavijest se više ne otvara nigdje."
+ },
"action": {
"openSession": "Otvori sesiju"
},
diff --git a/apps/mobile/src/i18n/locales/ca.json b/apps/mobile/src/i18n/locales/ca.json
index a7b33378cda..30c1ddbc168 100644
--- a/apps/mobile/src/i18n/locales/ca.json
+++ b/apps/mobile/src/i18n/locales/ca.json
@@ -896,6 +896,18 @@
"spendAlertsSubtitle": "superacions de llindars i pics horaris",
"spendAlertsToggle": "Categoria d'alertes de despesa"
},
+ "history": {
+ "title": "Historial de notificacions",
+ "subtitle": "Notificacions que ha rebut aquest compte",
+ "bannerBody": "Les notificacions push estan desactivades en aquest dispositiu, així que les noves no arribaran aquí.",
+ "bannerAction": "Configuració de notificacions",
+ "emptyTitle": "Encara no hi ha notificacions",
+ "emptyCategoryTitle": "No hi ha notificacions en aquesta categoria",
+ "emptyBody": "Les notificacions que rep aquest compte apareixen aquí. Es conserven durant {{days}} dies.",
+ "capped": "Es mostren les notificacions més recents.",
+ "loadError": "No s'ha pogut carregar l'historial de notificacions.",
+ "openFailed": "Aquesta notificació ja no s'obre enlloc."
+ },
"action": {
"openSession": "Obre la sessió"
},
diff --git a/apps/mobile/src/i18n/locales/ckb.json b/apps/mobile/src/i18n/locales/ckb.json
index 33a53e46f0b..d3ee772f668 100644
--- a/apps/mobile/src/i18n/locales/ckb.json
+++ b/apps/mobile/src/i18n/locales/ckb.json
@@ -877,6 +877,18 @@
"spendAlertsSubtitle": "تێپەڕینی سنوورەکان و بەرزبوونەوەکانی کاتژمێری",
"spendAlertsToggle": "پۆلی ئاگادارکردنەوەکانی خەرجی"
},
+ "history": {
+ "title": "مێژووی ئاگادارکردنەوەکان",
+ "subtitle": "ئاگادارکردنەوەکانی وەرگیراو لەم هەژمارە",
+ "bannerBody": "ئاگادارکردنەوەکانی پاڵنان بۆ ئەم ئامێرە ناچالاک کراون، بۆیە ئەوانەی نوێ لێرە ناگەن.",
+ "bannerAction": "ڕێکخستنەکانی ئاگادارکردنەوە",
+ "emptyTitle": "هێشتا هیچ ئاگادارکردنەوەیەک نییە",
+ "emptyCategoryTitle": "هیچ ئاگادارکردنەوەیەک لەم پۆلەدا نییە",
+ "emptyBody": "ئەو ئاگادارکردنەوانەی کە ئەم هەژمارە وەریان دەگرێت لێرە دەردەکەون. بۆ ماوەی {{days}} ڕۆژ پاشەکەوت دەکرێن.",
+ "capped": "نوێترین ئاگادارکردنەوەکان پیشان دەدرێن.",
+ "loadError": "نەتوانرا مێژووی ئاگادارکردنەوەکان بار بکرێت.",
+ "openFailed": "ئەم ئاگادارکردنەوەیە چیتر لە هیچ شوێنێک ناکرێتەوە."
+ },
"action": {
"openSession": "کردنەوەی سێشن"
},
diff --git a/apps/mobile/src/i18n/locales/cs.json b/apps/mobile/src/i18n/locales/cs.json
index fbdc0c27d6f..57cca71c426 100644
--- a/apps/mobile/src/i18n/locales/cs.json
+++ b/apps/mobile/src/i18n/locales/cs.json
@@ -915,6 +915,18 @@
"spendAlertsSubtitle": "překročení prahů a hodinové špičky",
"spendAlertsToggle": "Kategorie upozornění na útratu"
},
+ "history": {
+ "title": "Historie oznámení",
+ "subtitle": "Oznámení, která tento účet obdržel",
+ "bannerBody": "Push oznámení jsou pro toto zařízení vypnutá, takže nová sem nebudou přicházet.",
+ "bannerAction": "Nastavení oznámení",
+ "emptyTitle": "Zatím žádná oznámení",
+ "emptyCategoryTitle": "V této kategorii nejsou žádná oznámení",
+ "emptyBody": "Oznámení, která tento účet obdrží, se zobrazují zde. Uchovávají se {{days}} dní.",
+ "capped": "Zobrazují se nejnovější oznámení.",
+ "loadError": "Historii oznámení se nepodařilo načíst.",
+ "openFailed": "Toto oznámení se už nikde neotevře."
+ },
"action": {
"openSession": "Otevřít relaci"
},
diff --git a/apps/mobile/src/i18n/locales/cy.json b/apps/mobile/src/i18n/locales/cy.json
index 58908b2f8bc..017667737ed 100644
--- a/apps/mobile/src/i18n/locales/cy.json
+++ b/apps/mobile/src/i18n/locales/cy.json
@@ -953,6 +953,18 @@
"spendAlertsSubtitle": "croesi trothwyon a sbigau bob awr",
"spendAlertsToggle": "Categori rhybuddion gwariant"
},
+ "history": {
+ "title": "Hanes hysbysiadau",
+ "subtitle": "Hysbysiadau a dderbyniodd y cyfrif hwn",
+ "bannerBody": "Mae hysbysiadau gwthio wedi'u diffodd ar gyfer y ddyfais hon, felly ni fydd rhai newydd yn cyrraedd yma.",
+ "bannerAction": "Gosodiadau hysbysiadau",
+ "emptyTitle": "Dim hysbysiadau eto",
+ "emptyCategoryTitle": "Dim hysbysiadau yn y categori hwn",
+ "emptyBody": "Bydd hysbysiadau a ddaw i'r cyfrif hwn yn ymddangos yma. Cânt eu cadw am {{days}} diwrnod.",
+ "capped": "Yn dangos y hysbysiadau diweddaraf.",
+ "loadError": "Methu llwytho hanes hysbysiadau.",
+ "openFailed": "Nid yw'r hysbysiad hwn yn agor yn unman mwyach."
+ },
"action": {
"openSession": "Agor y sesiwn"
},
diff --git a/apps/mobile/src/i18n/locales/da.json b/apps/mobile/src/i18n/locales/da.json
index 6a2beb51d96..ea9e745db78 100644
--- a/apps/mobile/src/i18n/locales/da.json
+++ b/apps/mobile/src/i18n/locales/da.json
@@ -877,6 +877,18 @@
"spendAlertsSubtitle": "grænseoverskridelser og spikes pr. time",
"spendAlertsToggle": "Kategorien forbrugsadvarsler"
},
+ "history": {
+ "title": "Notifikationshistorik",
+ "subtitle": "Notifikationer, som denne konto har modtaget",
+ "bannerBody": "Pushnotifikationer er slået fra på denne enhed, så nye ankommer ikke her.",
+ "bannerAction": "Notifikationsindstillinger",
+ "emptyTitle": "Ingen notifikationer endnu",
+ "emptyCategoryTitle": "Ingen notifikationer i denne kategori",
+ "emptyBody": "Notifikationer, som denne konto modtager, vises her. De gemmes i {{days}} dage.",
+ "capped": "Viser de seneste notifikationer.",
+ "loadError": "Kunne ikke indlæse notifikationshistorikken.",
+ "openFailed": "Denne notifikation kan ikke længere åbnes nogen steder."
+ },
"action": {
"openSession": "Åbn session"
},
diff --git a/apps/mobile/src/i18n/locales/de.json b/apps/mobile/src/i18n/locales/de.json
index 2dad6938204..0288a2ce6e4 100644
--- a/apps/mobile/src/i18n/locales/de.json
+++ b/apps/mobile/src/i18n/locales/de.json
@@ -353,6 +353,18 @@
"spendAlertsSubtitle": "Schwellenwertüberschreitungen und stündliche Spitzen",
"spendAlertsToggle": "Kategorie „Ausgaben-Warnungen“"
},
+ "history": {
+ "title": "Benachrichtigungsverlauf",
+ "subtitle": "Benachrichtigungen, die dieses Konto erhalten hat",
+ "bannerBody": "Push-Benachrichtigungen sind für dieses Gerät deaktiviert, daher werden hier keine neuen eintreffen.",
+ "bannerAction": "Benachrichtigungseinstellungen",
+ "emptyTitle": "Noch keine Benachrichtigungen",
+ "emptyCategoryTitle": "Noch keine Benachrichtigungen in dieser Kategorie",
+ "emptyBody": "Benachrichtigungen, die dieses Konto erhält, erscheinen hier. Sie werden {{days}} Tage lang aufbewahrt.",
+ "capped": "Es werden die neuesten Benachrichtigungen angezeigt.",
+ "loadError": "Der Benachrichtigungsverlauf konnte nicht geladen werden.",
+ "openFailed": "Diese Benachrichtigung lässt sich nirgendwo mehr öffnen."
+ },
"action": {
"openSession": "Sitzung öffnen"
},
diff --git a/apps/mobile/src/i18n/locales/el.json b/apps/mobile/src/i18n/locales/el.json
index 6ef0405e565..cd6eafaced8 100644
--- a/apps/mobile/src/i18n/locales/el.json
+++ b/apps/mobile/src/i18n/locales/el.json
@@ -877,6 +877,18 @@
"spendAlertsSubtitle": "υπερβάσεις ορίων και ωριαίες κορυφώσεις",
"spendAlertsToggle": "Κατηγορία ειδοποιήσεων δαπανών"
},
+ "history": {
+ "title": "Ιστορικό ειδοποιήσεων",
+ "subtitle": "Ειδοποιήσεις που έλαβε αυτός ο λογαριασμός",
+ "bannerBody": "Οι ειδοποιήσεις push είναι απενεργοποιημένες για αυτή τη συσκευή, οπότε δεν θα φτάνουν νέες εδώ.",
+ "bannerAction": "Ρυθμίσεις ειδοποιήσεων",
+ "emptyTitle": "Δεν υπάρχουν ειδοποιήσεις ακόμη",
+ "emptyCategoryTitle": "Δεν υπάρχουν ειδοποιήσεις σε αυτή την κατηγορία",
+ "emptyBody": "Οι ειδοποιήσεις που λαμβάνει αυτός ο λογαριασμός εμφανίζονται εδώ. Διατηρούνται για {{days}} ημέρες.",
+ "capped": "Εμφανίζονται οι πιο πρόσφατες ειδοποιήσεις.",
+ "loadError": "Δεν ήταν δυνατή η φόρτωση του ιστορικού ειδοποιήσεων.",
+ "openFailed": "Αυτή η ειδοποίηση δεν ανοίγει πλέον πουθενά."
+ },
"action": {
"openSession": "Άνοιγμα συνεδρίας"
},
diff --git a/apps/mobile/src/i18n/locales/en.json b/apps/mobile/src/i18n/locales/en.json
index 84e21ada242..44af12722ba 100644
--- a/apps/mobile/src/i18n/locales/en.json
+++ b/apps/mobile/src/i18n/locales/en.json
@@ -971,6 +971,18 @@
"spendAlertsSubtitle": "threshold crossings and hourly spikes",
"spendAlertsToggle": "Spend alerts category"
},
+ "history": {
+ "title": "Notification history",
+ "subtitle": "Notifications this account received",
+ "bannerBody": "Push notifications are off for this device, so new ones will not arrive here.",
+ "bannerAction": "Notification settings",
+ "emptyTitle": "No notifications yet",
+ "emptyCategoryTitle": "No notifications in this category",
+ "emptyBody": "Notifications this account receives appear here. They are kept for {{days}} days.",
+ "capped": "Showing the most recent notifications.",
+ "loadError": "Could not load notification history.",
+ "openFailed": "This notification no longer opens anywhere."
+ },
"action": {
"openSession": "Open session"
},
diff --git a/apps/mobile/src/i18n/locales/es.json b/apps/mobile/src/i18n/locales/es.json
index 0f8aa097dc0..c33aa5ec624 100644
--- a/apps/mobile/src/i18n/locales/es.json
+++ b/apps/mobile/src/i18n/locales/es.json
@@ -326,6 +326,18 @@
"spendAlertsSubtitle": "superaciones de umbrales y picos por hora",
"spendAlertsToggle": "Categoría de alertas de gasto"
},
+ "history": {
+ "title": "Historial de notificaciones",
+ "subtitle": "Notificaciones que recibió esta cuenta",
+ "bannerBody": "Las notificaciones push están desactivadas en este dispositivo, por lo que las nuevas no llegarán aquí.",
+ "bannerAction": "Configuración de notificaciones",
+ "emptyTitle": "Aún no hay notificaciones",
+ "emptyCategoryTitle": "No hay notificaciones en esta categoría",
+ "emptyBody": "Las notificaciones que recibe esta cuenta aparecen aquí. Se conservan durante {{days}} días.",
+ "capped": "Mostrando las notificaciones más recientes.",
+ "loadError": "No se pudo cargar el historial de notificaciones.",
+ "openFailed": "Esta notificación ya no se puede abrir en ningún sitio."
+ },
"liveActivities": "Actividades en directo",
"liveActivitySubtitle": "Mostrar los agentes activos en la pantalla de bloqueo",
"liveUpdates": "Actualizaciones en tiempo real",
diff --git a/apps/mobile/src/i18n/locales/et.json b/apps/mobile/src/i18n/locales/et.json
index 682c5a63326..4cb4f167d65 100644
--- a/apps/mobile/src/i18n/locales/et.json
+++ b/apps/mobile/src/i18n/locales/et.json
@@ -877,6 +877,18 @@
"spendAlertsSubtitle": "lävendi ületamised ja tunnipõhised tõusud",
"spendAlertsToggle": "Kulutushoiatuste kategooria"
},
+ "history": {
+ "title": "Teavituste ajalugu",
+ "subtitle": "Teavitused, mille see konto sai",
+ "bannerBody": "Tõuketeavitused on selle seadme jaoks välja lülitatud, seega uued siia ei jõua.",
+ "bannerAction": "Teavituste seaded",
+ "emptyTitle": "Teavitusi veel pole",
+ "emptyCategoryTitle": "Selles kategoorias pole teavitusi",
+ "emptyBody": "Teavitused, mille see konto saab, kuvatakse siin. Neid säilitatakse {{days}} päeva.",
+ "capped": "Kuvatakse kõige hiljutisemad teavitused.",
+ "loadError": "Teavituste ajaloo laadimine ebaõnnestus.",
+ "openFailed": "See teavitus ei avane enam kuskil."
+ },
"action": {
"openSession": "Ava seanss"
},
diff --git a/apps/mobile/src/i18n/locales/eu.json b/apps/mobile/src/i18n/locales/eu.json
index 6778ed8992d..eba26cf48c6 100644
--- a/apps/mobile/src/i18n/locales/eu.json
+++ b/apps/mobile/src/i18n/locales/eu.json
@@ -877,6 +877,18 @@
"spendAlertsSubtitle": "atalaseen gainditzeak eta orduko gorakadak",
"spendAlertsToggle": "Gastu-alerten kategoria"
},
+ "history": {
+ "title": "Jakinarazpenen historia",
+ "subtitle": "Kontu honek jasotako jakinarazpenak",
+ "bannerBody": "Push jakinarazpenak desgaituta daude gailu honetan, beraz, berriak ez dira hona iritsiko.",
+ "bannerAction": "Jakinarazpenen ezarpenak",
+ "emptyTitle": "Oraindik ez dago jakinarazpenik",
+ "emptyCategoryTitle": "Kategoria honetan ez dago jakinarazpenik",
+ "emptyBody": "Kontu honek jasotzen dituen jakinarazpenak hemen agertzen dira. {{days}} egunez gordetzen dira.",
+ "capped": "Azken jakinarazpenak erakusten dira.",
+ "loadError": "Ezin izan da jakinarazpenen historia kargatu.",
+ "openFailed": "Jakinarazpen hau jada ez da inon irekitzen."
+ },
"action": {
"openSession": "Ireki saioa"
},
diff --git a/apps/mobile/src/i18n/locales/fa.json b/apps/mobile/src/i18n/locales/fa.json
index b031dec0335..362e7d92e8d 100644
--- a/apps/mobile/src/i18n/locales/fa.json
+++ b/apps/mobile/src/i18n/locales/fa.json
@@ -877,6 +877,18 @@
"spendAlertsSubtitle": "عبور از آستانهها و جهشهای ساعتی",
"spendAlertsToggle": "دستهٔ هشدارهای هزینه"
},
+ "history": {
+ "title": "تاریخچه اعلانها",
+ "subtitle": "اعلانهایی که این حساب دریافت کرده است",
+ "bannerBody": "اعلانهای فوری برای این دستگاه غیرفعال هستند، بنابراین اعلانهای جدید به اینجا نمیرسند.",
+ "bannerAction": "تنظیمات اعلانها",
+ "emptyTitle": "هنوز اعلانی وجود ندارد",
+ "emptyCategoryTitle": "هیچ اعلانی در این دسته وجود ندارد",
+ "emptyBody": "اعلانهایی که این حساب دریافت میکند در اینجا نمایش داده میشوند. آنها به مدت {{days}} روز نگهداری میشوند.",
+ "capped": "جدیدترین اعلانها نمایش داده میشوند.",
+ "loadError": "بارگذاری تاریخچه اعلانها ممکن نشد.",
+ "openFailed": "این اعلان دیگر در هیچجا باز نمیشود."
+ },
"action": {
"openSession": "باز کردن نشست"
},
diff --git a/apps/mobile/src/i18n/locales/fi.json b/apps/mobile/src/i18n/locales/fi.json
index cf54a9d176c..371d5a414b0 100644
--- a/apps/mobile/src/i18n/locales/fi.json
+++ b/apps/mobile/src/i18n/locales/fi.json
@@ -877,6 +877,18 @@
"spendAlertsSubtitle": "raja-arvojen ylitykset ja tunneittaiset piikit",
"spendAlertsToggle": "Kulutusvaroitusten luokka"
},
+ "history": {
+ "title": "Ilmoitushistoria",
+ "subtitle": "Tämän tilin vastaanottamat ilmoitukset",
+ "bannerBody": "Push-ilmoitukset ovat pois päältä tällä laitteella, joten uudet ilmoitukset eivät saavu tänne.",
+ "bannerAction": "Ilmoitusasetukset",
+ "emptyTitle": "Ei vielä ilmoituksia",
+ "emptyCategoryTitle": "Ei ilmoituksia tässä kategoriassa",
+ "emptyBody": "Tämän tilin vastaanottamat ilmoitukset näkyvät täällä. Ne säilytetään {{days}} päivää.",
+ "capped": "Näytetään uusimmat ilmoitukset.",
+ "loadError": "Ilmoitushistoriaa ei voitu ladata.",
+ "openFailed": "Tämä ilmoitus ei enää avaudu missään."
+ },
"action": {
"openSession": "Avaa istunto"
},
diff --git a/apps/mobile/src/i18n/locales/fil.json b/apps/mobile/src/i18n/locales/fil.json
index f4845168d33..f5efe8fd535 100644
--- a/apps/mobile/src/i18n/locales/fil.json
+++ b/apps/mobile/src/i18n/locales/fil.json
@@ -877,6 +877,18 @@
"spendAlertsSubtitle": "mga paglampas sa hangganan at pagtaas bawat oras",
"spendAlertsToggle": "Kategorya ng mga babala sa gastos"
},
+ "history": {
+ "title": "Kasaysayan ng notipikasyon",
+ "subtitle": "Mga notipikasyong natanggap ng account na ito",
+ "bannerBody": "Naka-off ang mga push notification para sa device na ito, kaya wala nang bagong darating dito.",
+ "bannerAction": "Mga setting ng notipikasyon",
+ "emptyTitle": "Wala pang mga notipikasyon",
+ "emptyCategoryTitle": "Wala pang mga notipikasyon sa kategoryang ito",
+ "emptyBody": "Lumalabas dito ang mga notipikasyong natatanggap ng account na ito. Itinatago ang mga ito sa loob ng {{days}} araw.",
+ "capped": "Ipinapakita ang mga pinakabagong notipikasyon.",
+ "loadError": "Hindi ma-load ang kasaysayan ng notipikasyon.",
+ "openFailed": "Hindi na mabubuksan kahit saan ang notipikasyong ito."
+ },
"action": {
"openSession": "Buksan ang sesyon"
},
diff --git a/apps/mobile/src/i18n/locales/fr.json b/apps/mobile/src/i18n/locales/fr.json
index 9c2f0f0f4df..7fe09d31a1b 100644
--- a/apps/mobile/src/i18n/locales/fr.json
+++ b/apps/mobile/src/i18n/locales/fr.json
@@ -294,6 +294,18 @@
"spendAlertsSubtitle": "franchissements de seuils et pics horaires",
"spendAlertsToggle": "Catégorie « Alertes de dépenses »"
},
+ "history": {
+ "title": "Historique des notifications",
+ "subtitle": "Notifications reçues par ce compte",
+ "bannerBody": "Les notifications push sont désactivées pour cet appareil, les nouvelles n'arriveront donc pas ici.",
+ "bannerAction": "Paramètres des notifications",
+ "emptyTitle": "Aucune notification pour l'instant",
+ "emptyCategoryTitle": "Aucune notification dans cette catégorie",
+ "emptyBody": "Les notifications reçues par ce compte apparaissent ici. Elles sont conservées pendant {{days}} jours.",
+ "capped": "Affichage des notifications les plus récentes.",
+ "loadError": "Impossible de charger l'historique des notifications.",
+ "openFailed": "Cette notification ne s'ouvre plus nulle part."
+ },
"liveActivities": "Activités en direct",
"liveActivitySubtitle": "Afficher les agents actifs sur l’écran verrouillé",
"liveUpdates": "Mises à jour en direct",
diff --git a/apps/mobile/src/i18n/locales/ga.json b/apps/mobile/src/i18n/locales/ga.json
index 32b42b7d374..57c39463e76 100644
--- a/apps/mobile/src/i18n/locales/ga.json
+++ b/apps/mobile/src/i18n/locales/ga.json
@@ -934,6 +934,18 @@
"spendAlertsSubtitle": "sáruithe tairsí agus spuaiceanna uaire",
"spendAlertsToggle": "Catagóir foláirimh chaiteachais"
},
+ "history": {
+ "title": "Stair fógraí",
+ "subtitle": "Na fógraí a fuair an cuntas seo",
+ "bannerBody": "Tá fógraí push múchta don ghléas seo, mar sin ní thiocfaidh cinn nua anseo.",
+ "bannerAction": "Socruithe fógraí",
+ "emptyTitle": "Níl aon fhógraí fós",
+ "emptyCategoryTitle": "Níl aon fhógraí sa chatagóir seo",
+ "emptyBody": "Taispeántar na fógraí a fhaigheann an cuntas seo anseo. Coimeádtar iad ar feadh {{days}} lá.",
+ "capped": "Taispeántar na fógraí is déanaí.",
+ "loadError": "Níorbh fhéidir stair fógraí a lódáil.",
+ "openFailed": "Ní osclaíonn an fógra seo in áit ar bith a thuilleadh."
+ },
"action": {
"openSession": "Oscail an seisiún"
},
diff --git a/apps/mobile/src/i18n/locales/gl.json b/apps/mobile/src/i18n/locales/gl.json
index 211b877c7f6..7ab66c2387e 100644
--- a/apps/mobile/src/i18n/locales/gl.json
+++ b/apps/mobile/src/i18n/locales/gl.json
@@ -877,6 +877,18 @@
"spendAlertsSubtitle": "superacións de limiares e picos por hora",
"spendAlertsToggle": "Categoría de alertas de gasto"
},
+ "history": {
+ "title": "Historial de notificacións",
+ "subtitle": "Notificacións recibidas por esta conta",
+ "bannerBody": "As notificacións push están desactivadas neste dispositivo, polo que non chegarán novas aquí.",
+ "bannerAction": "Configuración de notificacións",
+ "emptyTitle": "Aínda non hai notificacións",
+ "emptyCategoryTitle": "Non hai notificacións nesta categoría",
+ "emptyBody": "As notificacións que recibe esta conta aparecen aquí. Consérvanse durante {{days}} días.",
+ "capped": "Mostrando as notificacións máis recentes.",
+ "loadError": "Non se puido cargar o historial de notificacións.",
+ "openFailed": "Esta notificación xa non se abre desde ningún sitio."
+ },
"action": {
"openSession": "Abrir a sesión"
},
diff --git a/apps/mobile/src/i18n/locales/gu.json b/apps/mobile/src/i18n/locales/gu.json
index d84650f0a71..f20963d8f3a 100644
--- a/apps/mobile/src/i18n/locales/gu.json
+++ b/apps/mobile/src/i18n/locales/gu.json
@@ -877,6 +877,18 @@
"spendAlertsSubtitle": "મર્યાદા ઓળંગવી અને કલાકદીઠ ઉછાળા",
"spendAlertsToggle": "ખર્ચની ચેતવણીની શ્રેણી"
},
+ "history": {
+ "title": "સૂચનાનો ઇતિહાસ",
+ "subtitle": "આ ખાતાને મળેલી સૂચનાઓ",
+ "bannerBody": "આ ઉપકરણ માટે પુશ સૂચનાઓ બંધ છે, તેથી નવી સૂચનાઓ અહીં આવશે નહીં.",
+ "bannerAction": "સૂચના સેટિંગ્સ",
+ "emptyTitle": "હજી સુધી કોઈ સૂચના નથી",
+ "emptyCategoryTitle": "આ શ્રેણીમાં કોઈ સૂચના નથી",
+ "emptyBody": "આ ખાતાને મળતી સૂચનાઓ અહીં દેખાય છે. તે {{days}} દિવસ સુધી રાખવામાં આવે છે.",
+ "capped": "સૌથી તાજેતરની સૂચનાઓ દર્શાવવામાં આવે છે.",
+ "loadError": "સૂચનાનો ઇતિહાસ લોડ કરી શકાયો નહીં.",
+ "openFailed": "આ સૂચના હવે ક્યાંય ખૂલતી નથી."
+ },
"action": {
"openSession": "સેશન ખોલો"
},
diff --git a/apps/mobile/src/i18n/locales/ha.json b/apps/mobile/src/i18n/locales/ha.json
index 328932eb78e..9f9aa75b50b 100644
--- a/apps/mobile/src/i18n/locales/ha.json
+++ b/apps/mobile/src/i18n/locales/ha.json
@@ -877,6 +877,18 @@
"spendAlertsSubtitle": "wucewar iyaka da hauhawar sa'a",
"spendAlertsToggle": "Rukunin faɗakarwar kashe kuɗi"
},
+ "history": {
+ "title": "Tarihin sanarwa",
+ "subtitle": "Sanarwar da wannan asusu ya karɓa",
+ "bannerBody": "An kashe sanarwar turawa a wannan na'urar, don haka sabbin ba za su isa nan ba.",
+ "bannerAction": "Saitunan sanarwa",
+ "emptyTitle": "Babu sanarwa tukuna",
+ "emptyCategoryTitle": "Babu sanarwa a wannan rukunin",
+ "emptyBody": "Sanarwar da wannan asusu ke karɓa suna bayyana a nan. Ana ajiye su na kwanaki {{days}}.",
+ "capped": "Ana nuna sanarwar baya-bayan nan.",
+ "loadError": "An kasa loda tarihin sanarwa.",
+ "openFailed": "Wannan sanarwa ba ta ƙara buɗewa a ko'ina ba."
+ },
"action": {
"openSession": "Buɗe zaman"
},
diff --git a/apps/mobile/src/i18n/locales/he.json b/apps/mobile/src/i18n/locales/he.json
index d787ff79ecc..470c50bf80f 100644
--- a/apps/mobile/src/i18n/locales/he.json
+++ b/apps/mobile/src/i18n/locales/he.json
@@ -353,6 +353,18 @@
"spendAlertsSubtitle": "חציית ספים וזינוקים שעתיים",
"spendAlertsToggle": "קטגוריית התראות הוצאות"
},
+ "history": {
+ "title": "היסטוריית התראות",
+ "subtitle": "התראות שהחשבון הזה קיבל",
+ "bannerBody": "התראות דחיפה מושבתות במכשיר הזה, כך שלא יגיעו לכאן התראות חדשות.",
+ "bannerAction": "הגדרות התראות",
+ "emptyTitle": "אין עדיין התראות",
+ "emptyCategoryTitle": "אין התראות בקטגוריה הזו",
+ "emptyBody": "התראות שהחשבון הזה מקבל מופיעות כאן. הן נשמרות {{days}} ימים.",
+ "capped": "מוצגות ההתראות העדכניות ביותר.",
+ "loadError": "לא ניתן היה לטעון את היסטוריית ההתראות.",
+ "openFailed": "התראה זו כבר לא נפתחת בשום מקום."
+ },
"action": {
"openSession": "פתיחת סשן"
},
diff --git a/apps/mobile/src/i18n/locales/hi.json b/apps/mobile/src/i18n/locales/hi.json
index a814affc4b7..8831dd2f619 100644
--- a/apps/mobile/src/i18n/locales/hi.json
+++ b/apps/mobile/src/i18n/locales/hi.json
@@ -353,6 +353,18 @@
"spendAlertsSubtitle": "सीमा पार होना और हर घंटे की बढ़त",
"spendAlertsToggle": "खर्च की चेतावनी श्रेणी"
},
+ "history": {
+ "title": "सूचनाओं का इतिहास",
+ "subtitle": "इस खाते को मिली सूचनाएँ",
+ "bannerBody": "इस डिवाइस के लिए पुश सूचनाएँ बंद हैं, इसलिए नई सूचनाएँ यहाँ नहीं आएँगी।",
+ "bannerAction": "सूचना सेटिंग्स",
+ "emptyTitle": "अभी तक कोई सूचना नहीं",
+ "emptyCategoryTitle": "इस श्रेणी में कोई सूचना नहीं",
+ "emptyBody": "इस खाते को मिलने वाली सूचनाएँ यहाँ दिखाई देती हैं। इन्हें {{days}} दिनों तक रखा जाता है।",
+ "capped": "सबसे हाल की सूचनाएँ दिखाई जा रही हैं।",
+ "loadError": "सूचनाओं का इतिहास लोड नहीं हो सका।",
+ "openFailed": "यह सूचना अब कहीं नहीं खुलती।"
+ },
"action": {
"openSession": "सत्र खोलें"
},
diff --git a/apps/mobile/src/i18n/locales/hr.json b/apps/mobile/src/i18n/locales/hr.json
index 3e03fefa0c0..3b5a05bbe71 100644
--- a/apps/mobile/src/i18n/locales/hr.json
+++ b/apps/mobile/src/i18n/locales/hr.json
@@ -896,6 +896,18 @@
"spendAlertsSubtitle": "prekoračenja pragova i skokovi po satu",
"spendAlertsToggle": "Kategorija upozorenja o potrošnji"
},
+ "history": {
+ "title": "Povijest obavijesti",
+ "subtitle": "Obavijesti koje je ovaj račun primio",
+ "bannerBody": "Push obavijesti su isključene za ovaj uređaj pa nove neće stizati ovdje.",
+ "bannerAction": "Postavke obavijesti",
+ "emptyTitle": "Još nema obavijesti",
+ "emptyCategoryTitle": "Nema obavijesti u ovoj kategoriji",
+ "emptyBody": "Obavijesti koje ovaj račun prima pojavljuju se ovdje. Čuvaju se {{days}} dana.",
+ "capped": "Prikazuju se najnovije obavijesti.",
+ "loadError": "Nije moguće učitati povijest obavijesti.",
+ "openFailed": "Ova se obavijest više ne otvara nigdje."
+ },
"action": {
"openSession": "Otvori sesiju"
},
diff --git a/apps/mobile/src/i18n/locales/ht.json b/apps/mobile/src/i18n/locales/ht.json
index 301ed8a8527..c97cbd975b6 100644
--- a/apps/mobile/src/i18n/locales/ht.json
+++ b/apps/mobile/src/i18n/locales/ht.json
@@ -877,6 +877,18 @@
"spendAlertsSubtitle": "depasman papòt ak ogmantasyon chak èdtan",
"spendAlertsToggle": "Kategori alèt depans"
},
+ "history": {
+ "title": "Istwa notifikasyon",
+ "subtitle": "Notifikasyon kont sa a te resevwa",
+ "bannerBody": "Notifikasyon push dezaktive pou aparèy sa a, donk nouvo yo p ap rive isit la.",
+ "bannerAction": "Paramèt notifikasyon",
+ "emptyTitle": "Poko gen notifikasyon",
+ "emptyCategoryTitle": "Poko gen notifikasyon nan kategori sa a",
+ "emptyBody": "Notifikasyon kont sa a resevwa yo parèt isit la. Yo kenbe yo pandan {{days}} jou.",
+ "capped": "Sa yo se notifikasyon ki pi resan yo.",
+ "loadError": "Nou pa t ka chaje istwa notifikasyon yo.",
+ "openFailed": "Notifikasyon sa a pa ouvri okenn kote ankò."
+ },
"action": {
"openSession": "Louvri sesyon an"
},
diff --git a/apps/mobile/src/i18n/locales/hu.json b/apps/mobile/src/i18n/locales/hu.json
index 538fe36ccda..a6f7e5566aa 100644
--- a/apps/mobile/src/i18n/locales/hu.json
+++ b/apps/mobile/src/i18n/locales/hu.json
@@ -877,6 +877,18 @@
"spendAlertsSubtitle": "küszöbátlépések és óránkénti kiugrások",
"spendAlertsToggle": "Költésfigyelmeztetések kategóriája"
},
+ "history": {
+ "title": "Értesítési előzmények",
+ "subtitle": "Az ehhez a fiókhoz beérkezett értesítések",
+ "bannerBody": "A push értesítések ki vannak kapcsolva ezen az eszközön, ezért az új értesítések nem érkeznek meg ide.",
+ "bannerAction": "Értesítési beállítások",
+ "emptyTitle": "Még nincsenek értesítések",
+ "emptyCategoryTitle": "Ebben a kategóriában nincsenek értesítések",
+ "emptyBody": "Az ehhez a fiókhoz beérkező értesítések itt jelennek meg. {{days}} napig maradnak meg.",
+ "capped": "A legutóbbi értesítések láthatók.",
+ "loadError": "Nem sikerült betölteni az értesítési előzményeket.",
+ "openFailed": "Ez az értesítés már sehol nem nyitható meg."
+ },
"action": {
"openSession": "Munkamenet megnyitása"
},
diff --git a/apps/mobile/src/i18n/locales/hy.json b/apps/mobile/src/i18n/locales/hy.json
index ab0290ea24f..b051a4d29bf 100644
--- a/apps/mobile/src/i18n/locales/hy.json
+++ b/apps/mobile/src/i18n/locales/hy.json
@@ -877,6 +877,18 @@
"spendAlertsSubtitle": "շեմերի հատումներ և ժամային ցատկեր",
"spendAlertsToggle": "Ծախսերի ծանուցումների կատեգորիա"
},
+ "history": {
+ "title": "Ծանուցումների պատմություն",
+ "subtitle": "Ծանուցումներ, որոնք ստացել է այս հաշիվը",
+ "bannerBody": "Փուշ ծանուցումներն անջատված են այս սարքի համար, ուստի նորերը այստեղ չեն հասնի։",
+ "bannerAction": "Ծանուցումների կարգավորումներ",
+ "emptyTitle": "Դեռ ծանուցումներ չկան",
+ "emptyCategoryTitle": "Այս կատեգորիայում ծանուցումներ չկան",
+ "emptyBody": "Ծանուցումները, որոնք ստանում է այս հաշիվը, հայտնվում են այստեղ։ Դրանք պահվում են {{days}} օր։",
+ "capped": "Ցուցադրվում են ամենավերջին ծանուցումները։",
+ "loadError": "Չհաջողվեց բեռնել ծանուցումների պատմությունը։",
+ "openFailed": "Այս ծանուցումն այլևս ոչ մի տեղ չի բացվում։"
+ },
"action": {
"openSession": "Բացել նիստը"
},
diff --git a/apps/mobile/src/i18n/locales/id.json b/apps/mobile/src/i18n/locales/id.json
index 2d586ea3dcc..06e1b54ae89 100644
--- a/apps/mobile/src/i18n/locales/id.json
+++ b/apps/mobile/src/i18n/locales/id.json
@@ -353,6 +353,18 @@
"spendAlertsSubtitle": "melewati ambang batas dan lonjakan per jam",
"spendAlertsToggle": "Kategori peringatan pengeluaran"
},
+ "history": {
+ "title": "Riwayat notifikasi",
+ "subtitle": "Notifikasi yang diterima akun ini",
+ "bannerBody": "Notifikasi push nonaktif untuk perangkat ini, jadi notifikasi baru tidak akan masuk di sini.",
+ "bannerAction": "Pengaturan notifikasi",
+ "emptyTitle": "Belum ada notifikasi",
+ "emptyCategoryTitle": "Tidak ada notifikasi di kategori ini",
+ "emptyBody": "Notifikasi yang diterima akun ini akan muncul di sini. Notifikasi disimpan selama {{days}} hari.",
+ "capped": "Menampilkan notifikasi terbaru.",
+ "loadError": "Tidak dapat memuat riwayat notifikasi.",
+ "openFailed": "Notifikasi ini tidak lagi dapat dibuka di mana pun."
+ },
"action": {
"openSession": "Buka sesi"
},
diff --git a/apps/mobile/src/i18n/locales/ig.json b/apps/mobile/src/i18n/locales/ig.json
index 878f4f750a3..c394bdf6d66 100644
--- a/apps/mobile/src/i18n/locales/ig.json
+++ b/apps/mobile/src/i18n/locales/ig.json
@@ -877,6 +877,18 @@
"spendAlertsSubtitle": "ịgafe oke na mmụba kwa awa",
"spendAlertsToggle": "Otu ọkwa gbasara mmefu"
},
+ "history": {
+ "title": "Akụkọ ọkwa",
+ "subtitle": "Ọkwa akaụntụ a nwetara",
+ "bannerBody": "Agbanyụla ọkwa push maka ngwaọrụ a, yabụ ndị ọhụrụ agaghị abịa ebe a.",
+ "bannerAction": "Ntọala ọkwa",
+ "emptyTitle": "Ọnwebeghị ọkwa",
+ "emptyCategoryTitle": "Ọnwebeghị ọkwa n'ụdị a",
+ "emptyBody": "Ọkwa akaụntụ a na-anweta na-apụta ebe a. A na-edebe ha maka ụbọchị {{days}}.",
+ "capped": "Na-egosi ọkwa ndị kachasị ọhụrụ.",
+ "loadError": "Enweghị ike ịbute akụkọ ọkwa.",
+ "openFailed": "Ọkwa a anaghịzi emeghe ebe ọ bụla."
+ },
"action": {
"openSession": "Mepee nnọkọ"
},
diff --git a/apps/mobile/src/i18n/locales/is.json b/apps/mobile/src/i18n/locales/is.json
index df65508082a..9a42e507ec8 100644
--- a/apps/mobile/src/i18n/locales/is.json
+++ b/apps/mobile/src/i18n/locales/is.json
@@ -877,6 +877,18 @@
"spendAlertsSubtitle": "yfirferðir þröskulda og uppsveiflur á klukkustund",
"spendAlertsToggle": "Flokkur útgjaldaviðvarana"
},
+ "history": {
+ "title": "Tilkynningasaga",
+ "subtitle": "Tilkynningar sem þessi reikningur fékk",
+ "bannerBody": "Slökkt er á push-tilkynningum fyrir þetta tæki, svo nýjar munu ekki berast hingað.",
+ "bannerAction": "Stillingar tilkynninga",
+ "emptyTitle": "Engar tilkynningar ennþá",
+ "emptyCategoryTitle": "Engar tilkynningar í þessum flokki",
+ "emptyBody": "Tilkynningar sem þessi reikningur fær birtast hér. Þær eru geymdar í {{days}} daga.",
+ "capped": "Nýjustu tilkynningarnar eru sýndar.",
+ "loadError": "Ekki tókst að hlaða tilkynningasögu.",
+ "openFailed": "Þessi tilkynning opnast hvergi lengur."
+ },
"action": {
"openSession": "Opna lotu"
},
diff --git a/apps/mobile/src/i18n/locales/it.json b/apps/mobile/src/i18n/locales/it.json
index e9906e254ad..6d44c79bcc1 100644
--- a/apps/mobile/src/i18n/locales/it.json
+++ b/apps/mobile/src/i18n/locales/it.json
@@ -294,6 +294,18 @@
"spendAlertsSubtitle": "superamenti di soglia e picchi orari",
"spendAlertsToggle": "Categoria avvisi di spesa"
},
+ "history": {
+ "title": "Cronologia notifiche",
+ "subtitle": "Notifiche ricevute da questo account",
+ "bannerBody": "Le notifiche push sono disattivate per questo dispositivo, quindi le nuove non arriveranno qui.",
+ "bannerAction": "Impostazioni notifiche",
+ "emptyTitle": "Nessuna notifica per ora",
+ "emptyCategoryTitle": "Nessuna notifica in questa categoria",
+ "emptyBody": "Le notifiche ricevute da questo account compaiono qui. Vengono conservate per {{days}} giorni.",
+ "capped": "Vengono mostrate le notifiche più recenti.",
+ "loadError": "Impossibile caricare la cronologia delle notifiche.",
+ "openFailed": "Questa notifica non si apre più da nessuna parte."
+ },
"liveActivities": "Attività in tempo reale",
"liveActivitySubtitle": "Mostra gli agenti attivi nella schermata di blocco",
"liveUpdates": "Aggiornamenti in tempo reale",
diff --git a/apps/mobile/src/i18n/locales/ja.json b/apps/mobile/src/i18n/locales/ja.json
index 606ba5cae54..60ab36bd646 100644
--- a/apps/mobile/src/i18n/locales/ja.json
+++ b/apps/mobile/src/i18n/locales/ja.json
@@ -353,6 +353,18 @@
"spendAlertsSubtitle": "しきい値の超過と毎時の急増",
"spendAlertsToggle": "支出アラートのカテゴリ"
},
+ "history": {
+ "title": "通知履歴",
+ "subtitle": "このアカウントが受け取った通知",
+ "bannerBody": "このデバイスではプッシュ通知がオフになっているため、新しい通知はここに届きません。",
+ "bannerAction": "通知設定",
+ "emptyTitle": "通知はまだありません",
+ "emptyCategoryTitle": "このカテゴリの通知はありません",
+ "emptyBody": "このアカウントが受け取った通知はここに表示されます。{{days}}日間保存されます。",
+ "capped": "最新の通知を表示しています。",
+ "loadError": "通知履歴を読み込めませんでした。",
+ "openFailed": "この通知はもうどこからも開けません。"
+ },
"action": {
"openSession": "セッションを開く"
},
diff --git a/apps/mobile/src/i18n/locales/ka.json b/apps/mobile/src/i18n/locales/ka.json
index 3f1d44d37c2..b59ee65814a 100644
--- a/apps/mobile/src/i18n/locales/ka.json
+++ b/apps/mobile/src/i18n/locales/ka.json
@@ -877,6 +877,18 @@
"spendAlertsSubtitle": "ზღვრების გადაცილება და საათობრივი ნახტომები",
"spendAlertsToggle": "ხარჯვის შეტყობინებების კატეგორია"
},
+ "history": {
+ "title": "შეტყობინებების ისტორია",
+ "subtitle": "შეტყობინებები, რომლებიც ამ ანგარიშმა მიიღო",
+ "bannerBody": "Push შეტყობინებები გამორთულია ამ მოწყობილობისთვის, ამიტომ ახლები აქ არ მოვა.",
+ "bannerAction": "შეტყობინებების პარამეტრები",
+ "emptyTitle": "შეტყობინებები ჯერ არ არის",
+ "emptyCategoryTitle": "ამ კატეგორიაში შეტყობინებები არ არის",
+ "emptyBody": "შეტყობინებები, რომლებსაც ეს ანგარიში იღებს, აქ ჩნდება. ისინი ინახება {{days}} დღის განმავლობაში.",
+ "capped": "ნაჩვენებია უახლესი შეტყობინებები.",
+ "loadError": "შეტყობინებების ისტორიის ჩატვირთვა ვერ მოხერხდა.",
+ "openFailed": "ეს შეტყობინება აღარ იხსნება არსად."
+ },
"action": {
"openSession": "სესიის გახსნა"
},
diff --git a/apps/mobile/src/i18n/locales/kk.json b/apps/mobile/src/i18n/locales/kk.json
index 184934f2bd9..af8533e2e63 100644
--- a/apps/mobile/src/i18n/locales/kk.json
+++ b/apps/mobile/src/i18n/locales/kk.json
@@ -877,6 +877,18 @@
"spendAlertsSubtitle": "шектерден асу және сағаттық секірістер",
"spendAlertsToggle": "Шығыс ескертулері санаты"
},
+ "history": {
+ "title": "Хабарландырулар тарихы",
+ "subtitle": "Осы аккаунт алған хабарландырулар",
+ "bannerBody": "Бұл құрылғыда push-хабарландырулар өшірілген, сондықтан жаңалары осында келмейді.",
+ "bannerAction": "Хабарландыру баптаулары",
+ "emptyTitle": "Әзірге хабарландыру жоқ",
+ "emptyCategoryTitle": "Бұл санатта хабарландыру жоқ",
+ "emptyBody": "Осы аккаунт алған хабарландырулар осында көрсетіледі. Олар {{days}} күн бойы сақталады.",
+ "capped": "Ең соңғы хабарландырулар көрсетілуде.",
+ "loadError": "Хабарландырулар тарихын жүктеу мүмкін болмады.",
+ "openFailed": "Бұл хабарландыру енді еш жерде ашылмайды."
+ },
"action": {
"openSession": "Сессияны ашу"
},
diff --git a/apps/mobile/src/i18n/locales/km.json b/apps/mobile/src/i18n/locales/km.json
index c49bd289c31..e5ddb51be78 100644
--- a/apps/mobile/src/i18n/locales/km.json
+++ b/apps/mobile/src/i18n/locales/km.json
@@ -877,6 +877,18 @@
"spendAlertsSubtitle": "ការឆ្លងកាត់កម្រិត និងការកើនឡើងប្រចាំម៉ោង",
"spendAlertsToggle": "ប្រភេទនៃការជូនដំណឹងអំពីការចំណាយ"
},
+ "history": {
+ "title": "ប្រវត្តិការជូនដំណឹង",
+ "subtitle": "ការជូនដំណឹងដែលគណនីនេះបានទទួល",
+ "bannerBody": "ការជូនដំណឹងពន្លើសត្រូវបានបិទសម្រាប់ឧបករណ៍នេះ ដូច្នេះការជូនដំណឹងថ្មីៗនឹងមិនមកដល់ទីនេះទេ។",
+ "bannerAction": "ការកំណត់ការជូនដំណឹង",
+ "emptyTitle": "មិនទាន់មានការជូនដំណឹងនៅឡើយទេ",
+ "emptyCategoryTitle": "មិនទាន់មានការជូនដំណឹងនៅក្នុងប្រភេទនេះនៅឡើយទេ",
+ "emptyBody": "ការជូនដំណឹងដែលគណនីនេះទទួលបាននឹងបង្ហាញនៅទីនេះ។ ពួកវាត្រូវបានរក្សាទុកសម្រាប់រយៈពេល {{days}} ថ្ងៃ។",
+ "capped": "បង្ហាញការជូនដំណឹងថ្មីៗបំផុត។",
+ "loadError": "មិនអាចផ្ទុកប្រវត្តិការជូនដំណឹងបានទេ។",
+ "openFailed": "ការជូនដំណឹងនេះលែងបើកនៅកន្លែងណាទៀតហើយ។"
+ },
"action": {
"openSession": "បើកវគ្គ"
},
diff --git a/apps/mobile/src/i18n/locales/kn.json b/apps/mobile/src/i18n/locales/kn.json
index c605ea39f38..0a04613cbe2 100644
--- a/apps/mobile/src/i18n/locales/kn.json
+++ b/apps/mobile/src/i18n/locales/kn.json
@@ -877,6 +877,18 @@
"spendAlertsSubtitle": "ಮಿತಿ ದಾಟುವಿಕೆ ಮತ್ತು ಪ್ರತಿ ಗಂಟೆಯ ಏರಿಕೆಗಳು",
"spendAlertsToggle": "ಖರ್ಚಿನ ಎಚ್ಚರಿಕೆಗಳ ವರ್ಗ"
},
+ "history": {
+ "title": "ಅಧಿಸೂಚನೆ ಇತಿಹಾಸ",
+ "subtitle": "ಈ ಖಾತೆ ಸ್ವೀಕರಿಸಿದ ಅಧಿಸೂಚನೆಗಳು",
+ "bannerBody": "ಈ ಸಾಧನದಲ್ಲಿ ಪುಶ್ ಅಧಿಸೂಚನೆಗಳು ಆಫ್ ಆಗಿವೆ, ಆದ್ದರಿಂದ ಹೊಸ ಅಧಿಸೂಚನೆಗಳು ಇಲ್ಲಿ ಬರುವುದಿಲ್ಲ.",
+ "bannerAction": "ಅಧಿಸೂಚನೆ ಸೆಟ್ಟಿಂಗ್ಗಳು",
+ "emptyTitle": "ಇನ್ನೂ ಯಾವುದೇ ಅಧಿಸೂಚನೆಗಳಿಲ್ಲ",
+ "emptyCategoryTitle": "ಈ ವರ್ಗದಲ್ಲಿ ಯಾವುದೇ ಅಧಿಸೂಚನೆಗಳಿಲ್ಲ",
+ "emptyBody": "ಈ ಖಾತೆ ಸ್ವೀಕರಿಸುವ ಅಧಿಸೂಚನೆಗಳು ಇಲ್ಲಿ ಕಾಣಿಸಿಕೊಳ್ಳುತ್ತವೆ. ಅವುಗಳನ್ನು {{days}} ದಿನಗಳವರೆಗೆ ಇರಿಸಲಾಗುತ್ತದೆ.",
+ "capped": "ಅತ್ಯಂತ ಇತ್ತೀಚಿನ ಅಧಿಸೂಚನೆಗಳನ್ನು ತೋರಿಸಲಾಗುತ್ತಿದೆ.",
+ "loadError": "ಅಧಿಸೂಚನೆ ಇತಿಹಾಸವನ್ನು ಲೋಡ್ ಮಾಡಲು ಸಾಧ್ಯವಾಗಲಿಲ್ಲ.",
+ "openFailed": "ಈ ಅಧಿಸೂಚನೆ ಇನ್ನು ಮುಂದೆ ಎಲ್ಲಿಯೂ ತೆರೆಯುವುದಿಲ್ಲ."
+ },
"action": {
"openSession": "ಸೆಷನ್ ತೆರೆಯಿರಿ"
},
diff --git a/apps/mobile/src/i18n/locales/ko.json b/apps/mobile/src/i18n/locales/ko.json
index e336148de62..0d765e46cf6 100644
--- a/apps/mobile/src/i18n/locales/ko.json
+++ b/apps/mobile/src/i18n/locales/ko.json
@@ -353,6 +353,18 @@
"spendAlertsSubtitle": "임계값 초과 및 시간당 급증",
"spendAlertsToggle": "지출 알림 카테고리"
},
+ "history": {
+ "title": "알림 기록",
+ "subtitle": "이 계정이 받은 알림",
+ "bannerBody": "이 기기에서 푸시 알림이 꺼져 있어 새 알림이 여기에 도착하지 않습니다.",
+ "bannerAction": "알림 설정",
+ "emptyTitle": "아직 알림이 없습니다",
+ "emptyCategoryTitle": "이 카테고리에 알림이 없습니다",
+ "emptyBody": "이 계정이 받은 알림이 여기에 표시됩니다. {{days}}일 동안 보관됩니다.",
+ "capped": "최근 알림을 표시합니다.",
+ "loadError": "알림 기록을 불러오지 못했습니다.",
+ "openFailed": "이 알림은 더 이상 어디에서도 열 수 없습니다."
+ },
"action": {
"openSession": "세션 열기"
},
diff --git a/apps/mobile/src/i18n/locales/lo.json b/apps/mobile/src/i18n/locales/lo.json
index ea1dc311989..127efda5213 100644
--- a/apps/mobile/src/i18n/locales/lo.json
+++ b/apps/mobile/src/i18n/locales/lo.json
@@ -877,6 +877,18 @@
"spendAlertsSubtitle": "ການຂ້າມຂີດຈຳກັດ ແລະ ການເພີ່ມຂຶ້ນຕໍ່ຊົ່ວໂມງ",
"spendAlertsToggle": "ໝວດການແຈ້ງເຕືອນການໃຊ້ຈ່າຍ"
},
+ "history": {
+ "title": "ປະຫວັດການແຈ້ງເຕືອນ",
+ "subtitle": "ການແຈ້ງເຕືອນທີ່ບັນຊີນີ້ໄດ້ຮັບ",
+ "bannerBody": "ການແຈ້ງເຕືອນແບບພຸດຖືກປິດຢູ່ໃນອຸປະກອນນີ້, ດັ່ງນັ້ນການແຈ້ງເຕືອນໃໝ່ຈະບໍ່ມາຮອດນີ້.",
+ "bannerAction": "ການຕັ້ງຄ່າການແຈ້ງເຕືອນ",
+ "emptyTitle": "ຍັງບໍ່ມີການແຈ້ງເຕືອນ",
+ "emptyCategoryTitle": "ຍັງບໍ່ມີການແຈ້ງເຕືອນໃນໝວດໝູ່ນີ້",
+ "emptyBody": "ການແຈ້ງເຕືອນທີ່ບັນຊີນີ້ໄດ້ຮັບຈະສະແດງຢູ່ນີ້. ພວກມັນຈະຖືກເກັບໄວ້ {{days}} ມື້.",
+ "capped": "ກຳລັງສະແດງການແຈ້ງເຕືອນຫຼ້າສຸດ.",
+ "loadError": "ບໍ່ສາມາດໂຫຼດປະຫວັດການແຈ້ງເຕືອນໄດ້.",
+ "openFailed": "ການແຈ້ງເຕືອນນີ້ບໍ່ສາມາດເປີດໄດ້ອີກຕໍ່ໄປ."
+ },
"action": {
"openSession": "ເປີດເຊສຊັນ"
},
diff --git a/apps/mobile/src/i18n/locales/lt.json b/apps/mobile/src/i18n/locales/lt.json
index 5e39d338cf0..a2f3653e22a 100644
--- a/apps/mobile/src/i18n/locales/lt.json
+++ b/apps/mobile/src/i18n/locales/lt.json
@@ -915,6 +915,18 @@
"spendAlertsSubtitle": "slenksčių viršijimai ir valandiniai šuoliai",
"spendAlertsToggle": "Išlaidų įspėjimų kategorija"
},
+ "history": {
+ "title": "Pranešimų istorija",
+ "subtitle": "Pranešimai, kuriuos gavo ši paskyra",
+ "bannerBody": "Šiame įrenginyje stumiamieji pranešimai išjungti, todėl nauji pranešimai čia neatkeliaus.",
+ "bannerAction": "Pranešimų nustatymai",
+ "emptyTitle": "Kol kas pranešimų nėra",
+ "emptyCategoryTitle": "Šioje kategorijoje pranešimų nėra",
+ "emptyBody": "Pranešimai, kuriuos gauna ši paskyra, rodomi čia. Jie saugomi {{days}} dienų.",
+ "capped": "Rodomi naujausi pranešimai.",
+ "loadError": "Nepavyko įkelti pranešimų istorijos.",
+ "openFailed": "Šio pranešimo nebegalima atidaryti niekur."
+ },
"action": {
"openSession": "Atidaryti sesiją"
},
diff --git a/apps/mobile/src/i18n/locales/lv.json b/apps/mobile/src/i18n/locales/lv.json
index 2d96e261fe2..d4bff4839a8 100644
--- a/apps/mobile/src/i18n/locales/lv.json
+++ b/apps/mobile/src/i18n/locales/lv.json
@@ -896,6 +896,18 @@
"spendAlertsSubtitle": "sliekšņu pārsniegumi un stundas lēcieni",
"spendAlertsToggle": "Izdevumu brīdinājumu kategorija"
},
+ "history": {
+ "title": "Paziņojumu vēsture",
+ "subtitle": "Paziņojumi, ko šis konts ir saņēmis",
+ "bannerBody": "Push paziņojumi šajā ierīcē ir izslēgti, tāpēc jaunie šeit netiks saņemti.",
+ "bannerAction": "Paziņojumu iestatījumi",
+ "emptyTitle": "Vēl nav paziņojumu",
+ "emptyCategoryTitle": "Šajā kategorijā nav paziņojumu",
+ "emptyBody": "Paziņojumi, ko šis konts saņem, parādās šeit. Tie tiek glabāti {{days}} dienas.",
+ "capped": "Tiek rādīti jaunākie paziņojumi.",
+ "loadError": "Neizdevās ielādēt paziņojumu vēsturi.",
+ "openFailed": "Šis paziņojums vairs nekur neatveras."
+ },
"action": {
"openSession": "Atvērt sesiju"
},
diff --git a/apps/mobile/src/i18n/locales/mg.json b/apps/mobile/src/i18n/locales/mg.json
index 6cfd571a05b..9cd80711317 100644
--- a/apps/mobile/src/i18n/locales/mg.json
+++ b/apps/mobile/src/i18n/locales/mg.json
@@ -877,6 +877,18 @@
"spendAlertsSubtitle": "fihoaram-bidy sy fitsambikinana isan'ora",
"spendAlertsToggle": "Sokajin'ny fampitandremana momba ny fandaniana"
},
+ "history": {
+ "title": "Tantaran'ny fampandrenesana",
+ "subtitle": "Fampandrenesana voarain'ity kaonty ity",
+ "bannerBody": "Tsy mandeha ny fampandrenesana push amin'ity fitaovana ity, ka tsy hisy vaovao tonga eto.",
+ "bannerAction": "Fandrindran'ny fampandrenesana",
+ "emptyTitle": "Mbola tsy misy fampandrenesana",
+ "emptyCategoryTitle": "Tsy misy fampandrenesana ao amin'ity sokajy ity",
+ "emptyBody": "Miseho eto ny fampandrenesana azon'ity kaonty ity. Tehirizina mandritra ny {{days}} andro izy ireo.",
+ "capped": "Aseho ny fampandrenesana farany indrindra.",
+ "loadError": "Tsy nahomby ny fampakarana ny tantaran'ny fampandrenesana.",
+ "openFailed": "Tsy misokatra na aiza na aiza intsony io fampandrenesana io."
+ },
"action": {
"openSession": "Sokafy ny fotoam-piasana"
},
diff --git a/apps/mobile/src/i18n/locales/mi.json b/apps/mobile/src/i18n/locales/mi.json
index e36d0d7a3b7..a6d405c2c5e 100644
--- a/apps/mobile/src/i18n/locales/mi.json
+++ b/apps/mobile/src/i18n/locales/mi.json
@@ -877,6 +877,18 @@
"spendAlertsSubtitle": "ngā whitinga paepae me ngā pikinga ia hāora",
"spendAlertsToggle": "Kāwai matohi whakapaunga"
},
+ "history": {
+ "title": "Hītori whakamōhiotanga",
+ "subtitle": "Ngā whakamōhiotanga i riro i tēnei pūkete",
+ "bannerBody": "Kua weto ngā pānui pana mō tēnei taputapu, nō reira kāore e tae mai he pānui hou ki konei.",
+ "bannerAction": "Ngā tautuhinga whakamōhiotanga",
+ "emptyTitle": "Kāore anō he whakamōhiotanga",
+ "emptyCategoryTitle": "Kāore he whakamōhiotanga i tēnei kāwai",
+ "emptyBody": "Ka puta mai ki konei ngā whakamōhiotanga ka riro i tēnei pūkete. Ka tiakina mō {{days}} rā.",
+ "capped": "E whakaatu ana i ngā whakamōhiotanga tino hou.",
+ "loadError": "Kāore i taea te uta i te hītori whakamōhiotanga.",
+ "openFailed": "Kāore tēnei whakamōhiotanga e taea te whakatuwhera i tētahi wāhi ināianei."
+ },
"action": {
"openSession": "Whakatuwheratia te wātū"
},
diff --git a/apps/mobile/src/i18n/locales/mk.json b/apps/mobile/src/i18n/locales/mk.json
index 4c47a2c284e..cc21923778d 100644
--- a/apps/mobile/src/i18n/locales/mk.json
+++ b/apps/mobile/src/i18n/locales/mk.json
@@ -877,6 +877,18 @@
"spendAlertsSubtitle": "преминувања на прагови и часовни скокови",
"spendAlertsToggle": "Категорија предупредувања за трошење"
},
+ "history": {
+ "title": "Историја на известувања",
+ "subtitle": "Известувањата што ги прими оваа сметка",
+ "bannerBody": "Push известувањата се исклучени за овој уред, така што новите нема да пристигнат овде.",
+ "bannerAction": "Поставки за известувања",
+ "emptyTitle": "Сè уште нема известувања",
+ "emptyCategoryTitle": "Нема известувања во оваа категорија",
+ "emptyBody": "Известувањата што ги прима оваа сметка се појавуваат овде. Се чуваат {{days}} дена.",
+ "capped": "Се прикажуваат најновите известувања.",
+ "loadError": "Не успеа вчитувањето на историјата на известувања.",
+ "openFailed": "Ова известување веќе не се отвора никаде."
+ },
"action": {
"openSession": "Отвори сесија"
},
diff --git a/apps/mobile/src/i18n/locales/ml.json b/apps/mobile/src/i18n/locales/ml.json
index 335bf73e4be..12107db74ac 100644
--- a/apps/mobile/src/i18n/locales/ml.json
+++ b/apps/mobile/src/i18n/locales/ml.json
@@ -877,6 +877,18 @@
"spendAlertsSubtitle": "പരിധി ലംഘനങ്ങളും മണിക്കൂർ തോറുമുള്ള വർധനകളും",
"spendAlertsToggle": "ചെലവ് മുന്നറിയിപ്പ് വിഭാഗം"
},
+ "history": {
+ "title": "അറിയിപ്പ് ചരിത്രം",
+ "subtitle": "ഈ അക്കൗണ്ടിന് ലഭിച്ച അറിയിപ്പുകൾ",
+ "bannerBody": "ഈ ഉപകരണത്തിൽ പുഷ് അറിയിപ്പുകൾ ഓഫാണ്, അതിനാൽ പുതിയവ ഇവിടെ എത്തില്ല.",
+ "bannerAction": "അറിയിപ്പ് ക്രമീകരണങ്ങൾ",
+ "emptyTitle": "ഇതുവരെ അറിയിപ്പുകൾ ഇല്ല",
+ "emptyCategoryTitle": "ഈ വിഭാഗത്തിൽ അറിയിപ്പുകൾ ഇല്ല",
+ "emptyBody": "ഈ അക്കൗണ്ടിന് ലഭിക്കുന്ന അറിയിപ്പുകൾ ഇവിടെ ദൃശ്യമാകും. അവ {{days}} ദിവസത്തേക്ക് സൂക്ഷിക്കുന്നു.",
+ "capped": "ഏറ്റവും പുതിയ അറിയിപ്പുകൾ കാണിക്കുന്നു.",
+ "loadError": "അറിയിപ്പ് ചരിത്രം ലോഡ് ചെയ്യാനായില്ല.",
+ "openFailed": "ഈ അറിയിപ്പ് ഇനി എവിടെയും തുറക്കില്ല."
+ },
"action": {
"openSession": "സെഷൻ തുറക്കുക"
},
diff --git a/apps/mobile/src/i18n/locales/mn.json b/apps/mobile/src/i18n/locales/mn.json
index 0154a7fccc3..8e0b7118cf2 100644
--- a/apps/mobile/src/i18n/locales/mn.json
+++ b/apps/mobile/src/i18n/locales/mn.json
@@ -877,6 +877,18 @@
"spendAlertsSubtitle": "босго давсан болон цагийн огцом өсөлт",
"spendAlertsToggle": "Зарцуулалтын анхааруулгын ангилал"
},
+ "history": {
+ "title": "Мэдэгдлийн түүх",
+ "subtitle": "Энэ бүртгэлийн хүлээн авсан мэдэгдлүүд",
+ "bannerBody": "Энэ төхөөрөмж дээр push мэдэгдэл унтарсан тул шинэ мэдэгдэл энд ирэхгүй.",
+ "bannerAction": "Мэдэгдлийн тохиргоо",
+ "emptyTitle": "Одоогоор мэдэгдэл байхгүй",
+ "emptyCategoryTitle": "Энэ ангилалд мэдэгдэл байхгүй",
+ "emptyBody": "Энэ бүртгэлийн хүлээн авсан мэдэгдлүүд энд харагдана. Тэдгээрийг {{days}} хоног хадгална.",
+ "capped": "Хамгийн сүүлийн мэдэгдлүүдийг харуулж байна.",
+ "loadError": "Мэдэгдлийн түүхийг ачаалж чадсангүй.",
+ "openFailed": "Энэ мэдэгдэл хаана ч нээгдэхгүй болсон."
+ },
"action": {
"openSession": "Сесс нээх"
},
diff --git a/apps/mobile/src/i18n/locales/mr.json b/apps/mobile/src/i18n/locales/mr.json
index 975e503e1e2..32e29c90df3 100644
--- a/apps/mobile/src/i18n/locales/mr.json
+++ b/apps/mobile/src/i18n/locales/mr.json
@@ -877,6 +877,18 @@
"spendAlertsSubtitle": "मर्यादा ओलांडणे आणि प्रति तास वाढ",
"spendAlertsToggle": "खर्चाच्या सूचना श्रेणी"
},
+ "history": {
+ "title": "सूचनांचा इतिहास",
+ "subtitle": "या खात्याला मिळालेल्या सूचना",
+ "bannerBody": "या डिव्हाइसवर पुश सूचना बंद आहेत, त्यामुळे नवीन सूचना येथे येणार नाहीत.",
+ "bannerAction": "सूचना सेटिंग्ज",
+ "emptyTitle": "अद्याप सूचना नाहीत",
+ "emptyCategoryTitle": "या श्रेणीत सूचना नाहीत",
+ "emptyBody": "या खात्याला मिळणाऱ्या सूचना येथे दिसतात. त्या {{days}} दिवसांसाठी ठेवल्या जातात.",
+ "capped": "सर्वात अलीकडच्या सूचना दाखवल्या जात आहेत.",
+ "loadError": "सूचनांचा इतिहास लोड करता आला नाही.",
+ "openFailed": "ही सूचना आता कुठेही उघडत नाही."
+ },
"action": {
"openSession": "सत्र उघडा"
},
diff --git a/apps/mobile/src/i18n/locales/ms.json b/apps/mobile/src/i18n/locales/ms.json
index 1591c879719..bc7fe64a0e4 100644
--- a/apps/mobile/src/i18n/locales/ms.json
+++ b/apps/mobile/src/i18n/locales/ms.json
@@ -877,6 +877,18 @@
"spendAlertsSubtitle": "melepasi ambang dan lonjakan setiap jam",
"spendAlertsToggle": "Kategori amaran perbelanjaan"
},
+ "history": {
+ "title": "Sejarah pemberitahuan",
+ "subtitle": "Pemberitahuan yang diterima oleh akaun ini",
+ "bannerBody": "Pemberitahuan push dimatikan untuk peranti ini, jadi yang baharu tidak akan tiba di sini.",
+ "bannerAction": "Tetapan pemberitahuan",
+ "emptyTitle": "Belum ada pemberitahuan",
+ "emptyCategoryTitle": "Tiada pemberitahuan dalam kategori ini",
+ "emptyBody": "Pemberitahuan yang diterima oleh akaun ini muncul di sini. Ia disimpan selama {{days}} hari.",
+ "capped": "Memaparkan pemberitahuan terkini.",
+ "loadError": "Tidak dapat memuatkan sejarah pemberitahuan.",
+ "openFailed": "Pemberitahuan ini tidak lagi boleh dibuka di mana-mana."
+ },
"action": {
"openSession": "Buka sesi"
},
diff --git a/apps/mobile/src/i18n/locales/mt.json b/apps/mobile/src/i18n/locales/mt.json
index 6f5293eddea..af4f8376a5a 100644
--- a/apps/mobile/src/i18n/locales/mt.json
+++ b/apps/mobile/src/i18n/locales/mt.json
@@ -934,6 +934,18 @@
"spendAlertsSubtitle": "qbiż ta' limiti u żidiet fis-siegħa",
"spendAlertsToggle": "Kategorija tat-twissijiet tal-infiq"
},
+ "history": {
+ "title": "Kronoloġija tan-notifiki",
+ "subtitle": "In-notifiki li rċieva dan il-kont",
+ "bannerBody": "In-notifiki push huma mitfija għal dan l-apparat, għalhekk dawk ġodda mhux se jaslu hawn.",
+ "bannerAction": "Impostazzjonijiet tan-notifiki",
+ "emptyTitle": "Għad m'hemmx notifiki",
+ "emptyCategoryTitle": "Għad m'hemmx notifiki f'din il-kategorija",
+ "emptyBody": "In-notifiki li jirċievi dan il-kont jidhru hawn. Jinżammu għal {{days}} ġranet.",
+ "capped": "Qed jintwerew in-notifiki l-aktar reċenti.",
+ "loadError": "Ma setgħetx tiġi mgħobbija l-kronoloġija tan-notifiki.",
+ "openFailed": "Din in-notifika m'għadhiex tinfetaħ imkien."
+ },
"action": {
"openSession": "Iftaħ is-sessjoni"
},
diff --git a/apps/mobile/src/i18n/locales/my.json b/apps/mobile/src/i18n/locales/my.json
index c47a4001acf..88a12556164 100644
--- a/apps/mobile/src/i18n/locales/my.json
+++ b/apps/mobile/src/i18n/locales/my.json
@@ -877,6 +877,18 @@
"spendAlertsSubtitle": "သတ်မှတ်ချက် ကျော်လွန်မှုနှင့် နာရီအလိုက် မြင့်တက်မှု",
"spendAlertsToggle": "အသုံးစရိတ် သတိပေးချက် အမျိုးအစား"
},
+ "history": {
+ "title": "အကြောင်းကြားချက် မှတ်တမ်း",
+ "subtitle": "ဤအကောင့် လက်ခံရရှိသည့် အကြောင်းကြားချက်များ",
+ "bannerBody": "ဤစက်ပစ္စည်းအတွက် အကြောင်းကြားချက်များ ပိတ်ထားသည်၊ ထို့ကြောင့် အသစ်များ ဤနေရာသို့ မရောက်ရှိပါ။",
+ "bannerAction": "အကြောင်းကြားချက် ဆက်တင်များ",
+ "emptyTitle": "အကြောင်းကြားချက်များ မရှိသေးပါ",
+ "emptyCategoryTitle": "ဤအမျိုးအစားတွင် အကြောင်းကြားချက်များ မရှိပါ",
+ "emptyBody": "ဤအကောင့် လက်ခံရရှိသော အကြောင်းကြားချက်များ ဤနေရာတွင် ပေါ်လာပါသည်။ ၎င်းတို့ကို {{days}} ရက် သိမ်းဆည်းထားပါသည်။",
+ "capped": "နောက်ဆုံးရ အကြောင်းကြားချက်များကို ပြသနေပါသည်။",
+ "loadError": "အကြောင်းကြားချက် မှတ်တမ်းကို တင်၍မရပါ။",
+ "openFailed": "ဤအကြောင်းကြားချက်ကို မည်သည့်နေရာတွင်မျှ ဖွင့်၍မရတော့ပါ။"
+ },
"action": {
"openSession": "စက်ရှင်ကို ဖွင့်ပါ"
},
diff --git a/apps/mobile/src/i18n/locales/nb.json b/apps/mobile/src/i18n/locales/nb.json
index 94ff8271e08..db62049f307 100644
--- a/apps/mobile/src/i18n/locales/nb.json
+++ b/apps/mobile/src/i18n/locales/nb.json
@@ -877,6 +877,18 @@
"spendAlertsSubtitle": "grenseoverskridelser og topper per time",
"spendAlertsToggle": "Kategorien forbruksvarsler"
},
+ "history": {
+ "title": "Varslingshistorikk",
+ "subtitle": "Varsler denne kontoen har mottatt",
+ "bannerBody": "Pushvarsler er slått av for denne enheten, så nye varsler vil ikke komme hit.",
+ "bannerAction": "Varslingsinnstillinger",
+ "emptyTitle": "Ingen varsler ennå",
+ "emptyCategoryTitle": "Ingen varsler i denne kategorien",
+ "emptyBody": "Varsler denne kontoen mottar, vises her. De oppbevares i {{days}} dager.",
+ "capped": "Viser de nyeste varslene.",
+ "loadError": "Kunne ikke laste varslingshistorikk.",
+ "openFailed": "Dette varselet åpnes ikke lenger noe sted."
+ },
"action": {
"openSession": "Åpne økt"
},
diff --git a/apps/mobile/src/i18n/locales/ne.json b/apps/mobile/src/i18n/locales/ne.json
index 8d3330c8550..30691574f9b 100644
--- a/apps/mobile/src/i18n/locales/ne.json
+++ b/apps/mobile/src/i18n/locales/ne.json
@@ -877,6 +877,18 @@
"spendAlertsSubtitle": "सीमा नाघ्ने र प्रति घण्टा वृद्धि",
"spendAlertsToggle": "खर्चका चेतावनी श्रेणी"
},
+ "history": {
+ "title": "सूचना इतिहास",
+ "subtitle": "यो खाताले प्राप्त गरेका सूचनाहरू",
+ "bannerBody": "यो यन्त्रका लागि पुश सूचनाहरू बन्द छन्, त्यसैले नयाँ सूचनाहरू यहाँ आउँदैनन्।",
+ "bannerAction": "सूचना सेटिङहरू",
+ "emptyTitle": "अहिलेसम्म कुनै सूचना छैन",
+ "emptyCategoryTitle": "यो श्रेणीमा कुनै सूचना छैन",
+ "emptyBody": "यो खाताले प्राप्त गर्ने सूचनाहरू यहाँ देखिन्छन्। तिनीहरू {{days}} दिनसम्म राखिन्छन्।",
+ "capped": "सबैभन्दा पछिल्ला सूचनाहरू देखाइँदै छ।",
+ "loadError": "सूचना इतिहास लोड गर्न सकिएन।",
+ "openFailed": "यो सूचना अब कतै खुल्दैन।"
+ },
"action": {
"openSession": "सत्र खोल्नुहोस्"
},
diff --git a/apps/mobile/src/i18n/locales/nl.json b/apps/mobile/src/i18n/locales/nl.json
index 7068743475a..7893025a406 100644
--- a/apps/mobile/src/i18n/locales/nl.json
+++ b/apps/mobile/src/i18n/locales/nl.json
@@ -326,6 +326,18 @@
"spendAlertsSubtitle": "drempeloverschrijdingen en uurlijkse pieken",
"spendAlertsToggle": "Categorie bestedingswaarschuwingen"
},
+ "history": {
+ "title": "Meldingsgeschiedenis",
+ "subtitle": "Meldingen die dit account heeft ontvangen",
+ "bannerBody": "Pushmeldingen staan uit voor dit apparaat, dus nieuwe meldingen komen hier niet binnen.",
+ "bannerAction": "Meldingsinstellingen",
+ "emptyTitle": "Nog geen meldingen",
+ "emptyCategoryTitle": "Nog geen meldingen in deze categorie",
+ "emptyBody": "Meldingen die dit account ontvangt, verschijnen hier. Ze worden {{days}} dagen bewaard.",
+ "capped": "De meest recente meldingen worden weergegeven.",
+ "loadError": "Meldingsgeschiedenis kon niet worden geladen.",
+ "openFailed": "Deze melding opent nergens meer."
+ },
"liveActivities": "Liveactiviteiten",
"liveActivitySubtitle": "Actieve agents op het toegangsscherm tonen",
"liveUpdates": "Live-updates",
diff --git a/apps/mobile/src/i18n/locales/om.json b/apps/mobile/src/i18n/locales/om.json
index 9938e15cf98..795f53b4816 100644
--- a/apps/mobile/src/i18n/locales/om.json
+++ b/apps/mobile/src/i18n/locales/om.json
@@ -877,6 +877,18 @@
"spendAlertsSubtitle": "daarbii daangaa fi olka'iinsa sa'aatii",
"spendAlertsToggle": "Ramaddii akeekkachiisa baasii"
},
+ "history": {
+ "title": "Seenaa beeksisaa",
+ "subtitle": "Beeksisoota herregni kun argate",
+ "bannerBody": "Beeksisoonni push meeshaa kanaaf cufamaniiru, kanaaf haaraan asitti hin dhufan.",
+ "bannerAction": "Qindaa'ina beeksisaa",
+ "emptyTitle": "Ammatti beeksisoonni hin jiran",
+ "emptyCategoryTitle": "Beeksisoonni rammii kana keessatti hin jiran",
+ "emptyBody": "Beeksisoonni herregni kun fudhatu asitti mul'atu. Guyyaa {{days}}f ni eegamu.",
+ "capped": "Beeksisoonni haaraan agarsiifamaa jiran.",
+ "loadError": "Seenaa beeksisaa fiduu hin dandeenye.",
+ "openFailed": "Beeksisni kun amma bakka tokkollee hin banamu."
+ },
"action": {
"openSession": "Turtii bani"
},
diff --git a/apps/mobile/src/i18n/locales/or.json b/apps/mobile/src/i18n/locales/or.json
index 32664065bef..942fd4ed044 100644
--- a/apps/mobile/src/i18n/locales/or.json
+++ b/apps/mobile/src/i18n/locales/or.json
@@ -877,6 +877,18 @@
"spendAlertsSubtitle": "ସୀମା ଅତିକ୍ରମ ଏବଂ ଘଣ୍ଟାପ୍ରତି ବୃଦ୍ଧି",
"spendAlertsToggle": "ଖର୍ଚ୍ଚ ସତର୍କତା ବର୍ଗ"
},
+ "history": {
+ "title": "ବିଜ୍ଞପ୍ତି ଇତିହାସ",
+ "subtitle": "ଏହି ଖାତାରେ ଗ୍ରହଣ କରିଥିବା ବିଜ୍ଞପ୍ତି",
+ "bannerBody": "ଏହି ଡିଭାଇସ୍ ପାଇଁ ପୁଶ୍ ବିଜ୍ଞପ୍ତି ବନ୍ଦ ଅଛି, ତେଣୁ ନୂଆ ବିଜ୍ଞପ୍ତି ଏଠାରେ ଆସିବ ନାହିଁ।",
+ "bannerAction": "ବିଜ୍ଞପ୍ତି ସେଟିଂସ୍",
+ "emptyTitle": "ଏପର୍ଯ୍ୟନ୍ତ କୌଣସି ବିଜ୍ଞପ୍ତି ନାହିଁ",
+ "emptyCategoryTitle": "ଏହି ବର୍ଗରେ କୌଣସି ବିଜ୍ଞପ୍ତି ନାହିଁ",
+ "emptyBody": "ଏହି ଖାତାରେ ଗ୍ରହଣ କରୁଥିବା ବିଜ୍ଞପ୍ତିଗୁଡ଼ିକ ଏଠାରେ ଦେଖାଯିବ। ସେଗୁଡ଼ିକ {{days}} ଦିନ ପାଇଁ ରଖାଯାଏ।",
+ "capped": "ସର୍ବାଧିକ ନୂଆ ବିଜ୍ଞପ୍ତିଗୁଡ଼ିକ ଦେଖାଯାଉଛି।",
+ "loadError": "ବିଜ୍ଞପ୍ତି ଇତିହାସ ଲୋଡ୍ ହୋଇପାରିଲା ନାହିଁ।",
+ "openFailed": "ଏହି ବିଜ୍ଞପ୍ତି ଆଉ କୌଣସି ସ୍ଥାନରେ ଖୁଲିବ ନାହିଁ।"
+ },
"action": {
"openSession": "ସେସନ ଖୋଲନ୍ତୁ"
},
diff --git a/apps/mobile/src/i18n/locales/pa.json b/apps/mobile/src/i18n/locales/pa.json
index 884029537c6..7dac23383a0 100644
--- a/apps/mobile/src/i18n/locales/pa.json
+++ b/apps/mobile/src/i18n/locales/pa.json
@@ -877,6 +877,18 @@
"spendAlertsSubtitle": "ਸੀਮਾ ਟੱਪਣਾ ਅਤੇ ਹਰ ਘੰਟੇ ਵਾਧਾ",
"spendAlertsToggle": "ਖਰਚੇ ਦੀਆਂ ਚੇਤਾਵਨੀਆਂ ਦੀ ਸ਼੍ਰੇਣੀ"
},
+ "history": {
+ "title": "ਸੂਚਨਾਵਾਂ ਦਾ ਇਤਿਹਾਸ",
+ "subtitle": "ਇਸ ਖਾਤੇ ਨੂੰ ਮਿਲੀਆਂ ਸੂਚਨਾਵਾਂ",
+ "bannerBody": "ਇਸ ਡੀਵਾਈਸ ਲਈ ਪੁਸ਼ ਸੂਚਨਾਵਾਂ ਬੰਦ ਹਨ, ਇਸ ਲਈ ਨਵੀਆਂ ਸੂਚਨਾਵਾਂ ਇੱਥੇ ਨਹੀਂ ਆਉਣਗੀਆਂ।",
+ "bannerAction": "ਸੂਚਨਾ ਸੈਟਿੰਗਾਂ",
+ "emptyTitle": "ਹਾਲੇ ਕੋਈ ਸੂਚਨਾ ਨਹੀਂ",
+ "emptyCategoryTitle": "ਇਸ ਸ਼੍ਰੇਣੀ ਵਿੱਚ ਕੋਈ ਸੂਚਨਾ ਨਹੀਂ",
+ "emptyBody": "ਇਸ ਖਾਤੇ ਨੂੰ ਮਿਲਣ ਵਾਲੀਆਂ ਸੂਚਨਾਵਾਂ ਇੱਥੇ ਦਿਖਾਈ ਦਿੰਦੀਆਂ ਹਨ। ਇਹਨਾਂ ਨੂੰ {{days}} ਦਿਨਾਂ ਲਈ ਰੱਖਿਆ ਜਾਂਦਾ ਹੈ।",
+ "capped": "ਸਭ ਤੋਂ ਨਵੀਆਂ ਸੂਚਨਾਵਾਂ ਦਿਖਾਈਆਂ ਜਾ ਰਹੀਆਂ ਹਨ।",
+ "loadError": "ਸੂਚਨਾਵਾਂ ਦਾ ਇਤਿਹਾਸ ਲੋਡ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਿਆ।",
+ "openFailed": "ਇਹ ਸੂਚਨਾ ਹੁਣ ਕਿਤੇ ਵੀ ਨਹੀਂ ਖੁੱਲ੍ਹਦੀ।"
+ },
"action": {
"openSession": "ਸੈਸ਼ਨ ਖੋਲ੍ਹੋ"
},
diff --git a/apps/mobile/src/i18n/locales/pl.json b/apps/mobile/src/i18n/locales/pl.json
index 4f14fa44168..78f0e4be5e1 100644
--- a/apps/mobile/src/i18n/locales/pl.json
+++ b/apps/mobile/src/i18n/locales/pl.json
@@ -353,6 +353,18 @@
"spendAlertsSubtitle": "przekroczenia progów i skoki godzinowe",
"spendAlertsToggle": "Kategoria alertów o wydatkach"
},
+ "history": {
+ "title": "Historia powiadomień",
+ "subtitle": "Powiadomienia otrzymane przez to konto",
+ "bannerBody": "Powiadomienia push są wyłączone dla tego urządzenia, więc nowe nie będą tu przychodzić.",
+ "bannerAction": "Ustawienia powiadomień",
+ "emptyTitle": "Nie ma jeszcze powiadomień",
+ "emptyCategoryTitle": "Brak powiadomień w tej kategorii",
+ "emptyBody": "Powiadomienia otrzymywane przez to konto pojawiają się tutaj. Są przechowywane przez {{days}} dni.",
+ "capped": "Wyświetlane są najnowsze powiadomienia.",
+ "loadError": "Nie udało się wczytać historii powiadomień.",
+ "openFailed": "Tego powiadomienia nie można już nigdzie otworzyć."
+ },
"action": {
"openSession": "Otwórz sesję"
},
diff --git a/apps/mobile/src/i18n/locales/ps.json b/apps/mobile/src/i18n/locales/ps.json
index 1098a9dae9c..2e5d60ab4dd 100644
--- a/apps/mobile/src/i18n/locales/ps.json
+++ b/apps/mobile/src/i18n/locales/ps.json
@@ -877,6 +877,18 @@
"spendAlertsSubtitle": "د حدونو تېرېدل او ساعتي زیاتوالی",
"spendAlertsToggle": "د لګښت خبرتیاوو کټګوري"
},
+ "history": {
+ "title": "د خبرتیاوو تاریخچه",
+ "subtitle": "هغه خبرتیاوې چې دا حساب ترلاسه کړې دي",
+ "bannerBody": "د دې وسیلې لپاره پش خبرتیاوې بندې دي، نو نوي به دلته نه رسیږي.",
+ "bannerAction": "د خبرتیاوو تنظیمات",
+ "emptyTitle": "تر اوسه هېڅ خبرتیاوې نشته",
+ "emptyCategoryTitle": "په دې کټګورۍ کې هېڅ خبرتیاوې نشته",
+ "emptyBody": "هغه خبرتیاوې چې دا حساب ترلاسه کوي دلته ښکاري. دوی د {{days}} ورځو لپاره ساتل کیږي.",
+ "capped": "تر ټولو وروستي خبرتیاوې ښودل کیږي.",
+ "loadError": "د خبرتیاوو تاریخچه بار نه شوه.",
+ "openFailed": "دا خبرتیا نوره هېچیرته نه خلاصیږي."
+ },
"action": {
"openSession": "ناسته پرانیزئ"
},
diff --git a/apps/mobile/src/i18n/locales/pt-BR.json b/apps/mobile/src/i18n/locales/pt-BR.json
index 752e8463b49..4aee5bb3247 100644
--- a/apps/mobile/src/i18n/locales/pt-BR.json
+++ b/apps/mobile/src/i18n/locales/pt-BR.json
@@ -321,6 +321,18 @@
"spendAlertsSubtitle": "ultrapassagens de limites e picos por hora",
"spendAlertsToggle": "Categoria de alertas de gastos"
},
+ "history": {
+ "title": "Histórico de notificações",
+ "subtitle": "Notificações recebidas por esta conta",
+ "bannerBody": "As notificações push estão desativadas neste dispositivo, então novas notificações não chegarão aqui.",
+ "bannerAction": "Configurações de notificações",
+ "emptyTitle": "Nenhuma notificação ainda",
+ "emptyCategoryTitle": "Nenhuma notificação nesta categoria",
+ "emptyBody": "As notificações que esta conta recebe aparecem aqui. Elas são mantidas por {{days}} dias.",
+ "capped": "Mostrando as notificações mais recentes.",
+ "loadError": "Não foi possível carregar o histórico de notificações.",
+ "openFailed": "Esta notificação não abre mais em lugar nenhum."
+ },
"action": {
"openSession": "Abrir sessão"
},
diff --git a/apps/mobile/src/i18n/locales/pt.json b/apps/mobile/src/i18n/locales/pt.json
index 4f0f4a5abd0..226fc693f06 100644
--- a/apps/mobile/src/i18n/locales/pt.json
+++ b/apps/mobile/src/i18n/locales/pt.json
@@ -896,6 +896,18 @@
"spendAlertsSubtitle": "ultrapassagens de limites e picos horários",
"spendAlertsToggle": "Categoria de alertas de gastos"
},
+ "history": {
+ "title": "Histórico de notificações",
+ "subtitle": "Notificações que esta conta recebeu",
+ "bannerBody": "As notificações push estão desativadas neste dispositivo, por isso as novas não chegarão aqui.",
+ "bannerAction": "Definições de notificações",
+ "emptyTitle": "Ainda não há notificações",
+ "emptyCategoryTitle": "Ainda não há notificações nesta categoria",
+ "emptyBody": "As notificações que esta conta recebe aparecem aqui. São guardadas durante {{days}} dias.",
+ "capped": "A mostrar as notificações mais recentes.",
+ "loadError": "Não foi possível carregar o histórico de notificações.",
+ "openFailed": "Esta notificação já não abre em lado nenhum."
+ },
"action": {
"openSession": "Abrir sessão"
},
diff --git a/apps/mobile/src/i18n/locales/ro.json b/apps/mobile/src/i18n/locales/ro.json
index 0cd7e37d1ef..2f62a31a630 100644
--- a/apps/mobile/src/i18n/locales/ro.json
+++ b/apps/mobile/src/i18n/locales/ro.json
@@ -896,6 +896,18 @@
"spendAlertsSubtitle": "depășiri de praguri și vârfuri orare",
"spendAlertsToggle": "Categoria alerte de cheltuieli"
},
+ "history": {
+ "title": "Istoric notificări",
+ "subtitle": "Notificările primite de acest cont",
+ "bannerBody": "Notificările push sunt dezactivate pentru acest dispozitiv, așa că cele noi nu vor ajunge aici.",
+ "bannerAction": "Setări notificări",
+ "emptyTitle": "Nicio notificare încă",
+ "emptyCategoryTitle": "Nicio notificare în această categorie",
+ "emptyBody": "Notificările primite de acest cont apar aici. Acestea se păstrează pentru {{days}} zile.",
+ "capped": "Se afișează cele mai recente notificări.",
+ "loadError": "Nu s-a putut încărca istoricul notificărilor.",
+ "openFailed": "Această notificare nu se mai deschide nicăieri."
+ },
"action": {
"openSession": "Deschide sesiunea"
},
diff --git a/apps/mobile/src/i18n/locales/ru.json b/apps/mobile/src/i18n/locales/ru.json
index 36a99ba01a3..7499b2f8453 100644
--- a/apps/mobile/src/i18n/locales/ru.json
+++ b/apps/mobile/src/i18n/locales/ru.json
@@ -353,6 +353,18 @@
"spendAlertsSubtitle": "превышения порогов и почасовые всплески",
"spendAlertsToggle": "Категория «Оповещения о расходах»"
},
+ "history": {
+ "title": "История уведомлений",
+ "subtitle": "Уведомления, полученные этим аккаунтом",
+ "bannerBody": "Push-уведомления отключены на этом устройстве, поэтому новые не будут приходить сюда.",
+ "bannerAction": "Настройки уведомлений",
+ "emptyTitle": "Уведомлений пока нет",
+ "emptyCategoryTitle": "В этой категории уведомлений нет",
+ "emptyBody": "Уведомления, которые получает этот аккаунт, появляются здесь. Они хранятся {{days}} дней.",
+ "capped": "Показаны последние уведомления.",
+ "loadError": "Не удалось загрузить историю уведомлений.",
+ "openFailed": "Это уведомление больше нигде не открывается."
+ },
"action": {
"openSession": "Открыть сеанс"
},
diff --git a/apps/mobile/src/i18n/locales/si.json b/apps/mobile/src/i18n/locales/si.json
index 8d62fd2676c..2c031e219fa 100644
--- a/apps/mobile/src/i18n/locales/si.json
+++ b/apps/mobile/src/i18n/locales/si.json
@@ -877,6 +877,18 @@
"spendAlertsSubtitle": "සීමා ඉක්මවීම් සහ පැයකට ඇති උච්චතා",
"spendAlertsToggle": "වියදම් දැනුම්දීම් ප්රවර්ගය"
},
+ "history": {
+ "title": "දැනුම්දීම් ඉතිහාසය",
+ "subtitle": "මෙම ගිණුමට ලැබුණු දැනුම්දීම්",
+ "bannerBody": "මෙම උපාංගය සඳහා තල්ලු දැනුම්දීම් අක්රීය කර ඇත, එබැවින් නව ඒවා මෙහි නොපැමිණේ.",
+ "bannerAction": "දැනුම්දීම් සැකසීම්",
+ "emptyTitle": "තවම දැනුම්දීම් නැත",
+ "emptyCategoryTitle": "මෙම කාණ්ඩයේ දැනුම්දීම් නැත",
+ "emptyBody": "මෙම ගිණුමට ලැබෙන දැනුම්දීම් මෙහි දිස්වේ. ඒවා දින {{days}} ක් තබා ගනු ලැබේ.",
+ "capped": "නවතම දැනුම්දීම් පෙන්වයි.",
+ "loadError": "දැනුම්දීම් ඉතිහාසය පූරණය කිරීමට නොහැකි විය.",
+ "openFailed": "මෙම දැනුම්දීම තවදුරටත් කිසිදු තැනක විවෘත නොවේ."
+ },
"action": {
"openSession": "සැසිය විවෘත කරන්න"
},
diff --git a/apps/mobile/src/i18n/locales/sk.json b/apps/mobile/src/i18n/locales/sk.json
index 30f8bf9b68a..08798139e1d 100644
--- a/apps/mobile/src/i18n/locales/sk.json
+++ b/apps/mobile/src/i18n/locales/sk.json
@@ -915,6 +915,18 @@
"spendAlertsSubtitle": "prekročenia prahov a hodinové špičky",
"spendAlertsToggle": "Kategória upozornení na výdavky"
},
+ "history": {
+ "title": "História upozornení",
+ "subtitle": "Upozornenia, ktoré tento účet dostal",
+ "bannerBody": "Push upozornenia sú pre toto zariadenie vypnuté, takže sem nové nebudú prichádzať.",
+ "bannerAction": "Nastavenia upozornení",
+ "emptyTitle": "Zatiaľ žiadne upozornenia",
+ "emptyCategoryTitle": "Žiadne upozornenia v tejto kategórii",
+ "emptyBody": "Upozornenia, ktoré tento účet dostáva, sa zobrazujú tu. Uchovávajú sa {{days}} dní.",
+ "capped": "Zobrazujú sa najnovšie upozornenia.",
+ "loadError": "Históriu upozornení sa nepodarilo načítať.",
+ "openFailed": "Toto upozornenie sa už nikde neotvorí."
+ },
"action": {
"openSession": "Otvoriť reláciu"
},
diff --git a/apps/mobile/src/i18n/locales/sl.json b/apps/mobile/src/i18n/locales/sl.json
index 9564703661e..6a101a3966a 100644
--- a/apps/mobile/src/i18n/locales/sl.json
+++ b/apps/mobile/src/i18n/locales/sl.json
@@ -915,6 +915,18 @@
"spendAlertsSubtitle": "preseganja pragov in urni skoki",
"spendAlertsToggle": "Kategorija opozoril o porabi"
},
+ "history": {
+ "title": "Zgodovina obvestil",
+ "subtitle": "Obvestila, ki jih je prejel ta račun",
+ "bannerBody": "Potisna obvestila so za to napravo izklopljena, zato nova ne bodo prispela sem.",
+ "bannerAction": "Nastavitve obvestil",
+ "emptyTitle": "Ni še obvestil",
+ "emptyCategoryTitle": "Ni obvestil v tej kategoriji",
+ "emptyBody": "Obvestila, ki jih prejme ta račun, se prikažejo tukaj. Hranijo se {{days}} dni.",
+ "capped": "Prikazana so najnovejša obvestila.",
+ "loadError": "Zgodovine obvestil ni bilo mogoče naložiti.",
+ "openFailed": "To obvestilo se ne odpre več nikjer."
+ },
"action": {
"openSession": "Odpri sejo"
},
diff --git a/apps/mobile/src/i18n/locales/so.json b/apps/mobile/src/i18n/locales/so.json
index a7ce347dc16..daca0631e87 100644
--- a/apps/mobile/src/i18n/locales/so.json
+++ b/apps/mobile/src/i18n/locales/so.json
@@ -877,6 +877,18 @@
"spendAlertsSubtitle": "dhaafitaanka xadka iyo kor u kaca saacadlaha",
"spendAlertsToggle": "Qaybta digniinaha kharashka"
},
+ "history": {
+ "title": "Taariikhda ogeysiisyada",
+ "subtitle": "Ogeysiisyada ay heshay akoonkan",
+ "bannerBody": "Ogeysiisyada push waa dansan yihiin qalabkan, sidaas darteed kuwa cusub halkan kuma soo gaari doonaan.",
+ "bannerAction": "Dejinta ogeysiisyada",
+ "emptyTitle": "Weli ogeysiisyo ma jiraan",
+ "emptyCategoryTitle": "Qaybtaan ogeysiisyo ma jiraan",
+ "emptyBody": "Ogeysiisyada ay hesho akoonkan ayaa halkan ka muuqda. Waxaa la hayaa {{days}} maalmood.",
+ "capped": "Waxaa la muujinayaa ogeysiisyada ugu dambeeyay.",
+ "loadError": "Taariikhda ogeysiisyada lama soo dejin karin.",
+ "openFailed": "Ogeysiiskan mar dambe meelna lagama furi karo."
+ },
"action": {
"openSession": "Fur kalfadhi"
},
diff --git a/apps/mobile/src/i18n/locales/sq.json b/apps/mobile/src/i18n/locales/sq.json
index 90d9f675d6d..c09a3f66e75 100644
--- a/apps/mobile/src/i18n/locales/sq.json
+++ b/apps/mobile/src/i18n/locales/sq.json
@@ -877,6 +877,18 @@
"spendAlertsSubtitle": "tejkalime pragjesh dhe rritje për orë",
"spendAlertsToggle": "Kategoria e njoftimeve për shpenzimet"
},
+ "history": {
+ "title": "Historiku i njoftimeve",
+ "subtitle": "Njoftimet që ka marrë kjo llogari",
+ "bannerBody": "Njoftimet push janë të çaktivizuara për këtë pajisje, kështu që të rejat nuk do të mbërrijnë këtu.",
+ "bannerAction": "Cilësimet e njoftimeve",
+ "emptyTitle": "Nuk ka ende njoftime",
+ "emptyCategoryTitle": "Nuk ka njoftime në këtë kategori",
+ "emptyBody": "Njoftimet që merr kjo llogari shfaqen këtu. Ato ruhen për {{days}} ditë.",
+ "capped": "Po shfaqen njoftimet më të fundit.",
+ "loadError": "Nuk u arrit të ngarkohej historiku i njoftimeve.",
+ "openFailed": "Ky njoftim nuk hapet më askund."
+ },
"action": {
"openSession": "Hap seancën"
},
diff --git a/apps/mobile/src/i18n/locales/sr.json b/apps/mobile/src/i18n/locales/sr.json
index 40796ffaf95..49861fe9cbd 100644
--- a/apps/mobile/src/i18n/locales/sr.json
+++ b/apps/mobile/src/i18n/locales/sr.json
@@ -896,6 +896,18 @@
"spendAlertsSubtitle": "prekoračenja pragova i skokovi po satu",
"spendAlertsToggle": "Kategorija obaveštenja o potrošnji"
},
+ "history": {
+ "title": "Istorija obaveštenja",
+ "subtitle": "Obaveštenja koja je ovaj nalog primio",
+ "bannerBody": "Push obaveštenja su isključena za ovaj uređaj, pa nova neće stizati ovde.",
+ "bannerAction": "Podešavanja obaveštenja",
+ "emptyTitle": "Još nema obaveštenja",
+ "emptyCategoryTitle": "Nema obaveštenja u ovoj kategoriji",
+ "emptyBody": "Obaveštenja koja ovaj nalog prima prikazuju se ovde. Čuvaju se {{days}} dana.",
+ "capped": "Prikazuju se najnovija obaveštenja.",
+ "loadError": "Učitavanje istorije obaveštenja nije uspelo.",
+ "openFailed": "Ovo obaveštenje se više ne otvara nigde."
+ },
"action": {
"openSession": "Otvori sesiju"
},
diff --git a/apps/mobile/src/i18n/locales/sv.json b/apps/mobile/src/i18n/locales/sv.json
index 0e05616193f..52fff9648df 100644
--- a/apps/mobile/src/i18n/locales/sv.json
+++ b/apps/mobile/src/i18n/locales/sv.json
@@ -877,6 +877,18 @@
"spendAlertsSubtitle": "gränsöverskridanden och toppar per timme",
"spendAlertsToggle": "Kategorin utgiftsvarningar"
},
+ "history": {
+ "title": "Aviseringshistorik",
+ "subtitle": "Aviseringar som det här kontot har tagit emot",
+ "bannerBody": "Push-aviseringar är avstängda för den här enheten, så nya aviseringar kommer inte fram hit.",
+ "bannerAction": "Aviseringsinställningar",
+ "emptyTitle": "Inga aviseringar än",
+ "emptyCategoryTitle": "Inga aviseringar i den här kategorin",
+ "emptyBody": "Aviseringar som det här kontot tar emot visas här. De sparas i {{days}} dagar.",
+ "capped": "Visar de senaste aviseringarna.",
+ "loadError": "Kunde inte läsa in aviseringshistoriken.",
+ "openFailed": "Den här aviseringen öppnas inte längre någonstans."
+ },
"action": {
"openSession": "Öppna session"
},
diff --git a/apps/mobile/src/i18n/locales/sw.json b/apps/mobile/src/i18n/locales/sw.json
index 1c5b4c52d62..12d7d4c281f 100644
--- a/apps/mobile/src/i18n/locales/sw.json
+++ b/apps/mobile/src/i18n/locales/sw.json
@@ -877,6 +877,18 @@
"spendAlertsSubtitle": "kuvuka vizingiti na kuongezeka kwa saa",
"spendAlertsToggle": "Aina ya tahadhari za matumizi"
},
+ "history": {
+ "title": "Historia ya arifa",
+ "subtitle": "Arifa ambazo akaunti hii imepokea",
+ "bannerBody": "Arifa za push zimezimwa kwenye kifaa hiki, kwa hivyo mpya hazitafika hapa.",
+ "bannerAction": "Mipangilio ya arifa",
+ "emptyTitle": "Hakuna arifa bado",
+ "emptyCategoryTitle": "Hakuna arifa katika kundi hili",
+ "emptyBody": "Arifa ambazo akaunti hii inapokea zinaonekana hapa. Zinahifadhiwa kwa siku {{days}}.",
+ "capped": "Inaonyesha arifa za hivi karibuni.",
+ "loadError": "Imeshindikana kupakia historia ya arifa.",
+ "openFailed": "Arifa hii haifunguki tena popote."
+ },
"action": {
"openSession": "Fungua kikao"
},
diff --git a/apps/mobile/src/i18n/locales/ta.json b/apps/mobile/src/i18n/locales/ta.json
index 86697cbe809..b511543727d 100644
--- a/apps/mobile/src/i18n/locales/ta.json
+++ b/apps/mobile/src/i18n/locales/ta.json
@@ -877,6 +877,18 @@
"spendAlertsSubtitle": "வரம்பு மீறல்கள் மற்றும் மணிநேர உயர்வுகள்",
"spendAlertsToggle": "செலவு எச்சரிக்கை வகை"
},
+ "history": {
+ "title": "அறிவிப்பு வரலாறு",
+ "subtitle": "இந்தக் கணக்கு பெற்ற அறிவிப்புகள்",
+ "bannerBody": "இந்தச் சாதனத்தில் புஷ் அறிவிப்புகள் முடக்கப்பட்டுள்ளன, எனவே புதியவை இங்கு வராது.",
+ "bannerAction": "அறிவிப்பு அமைப்புகள்",
+ "emptyTitle": "இன்னும் அறிவிப்புகள் இல்லை",
+ "emptyCategoryTitle": "இந்தப் பிரிவில் அறிவிப்புகள் இல்லை",
+ "emptyBody": "இந்தக் கணக்கு பெறும் அறிவிப்புகள் இங்கே தோன்றும். அவை {{days}} நாட்களுக்கு வைக்கப்படும்.",
+ "capped": "மிகச் சமீபத்திய அறிவிப்புகள் காட்டப்படுகின்றன.",
+ "loadError": "அறிவிப்பு வரலாற்றை ஏற்ற முடியவில்லை.",
+ "openFailed": "இந்த அறிவிப்பு இனி எங்கும் திறக்காது."
+ },
"action": {
"openSession": "அமர்வைத் திறக்கவும்"
},
diff --git a/apps/mobile/src/i18n/locales/te.json b/apps/mobile/src/i18n/locales/te.json
index 567e3ad42c9..0974ddb2ae3 100644
--- a/apps/mobile/src/i18n/locales/te.json
+++ b/apps/mobile/src/i18n/locales/te.json
@@ -877,6 +877,18 @@
"spendAlertsSubtitle": "పరిమితి దాటడం మరియు గంటకు పెరుగుదల",
"spendAlertsToggle": "ఖర్చు హెచ్చరికల వర్గం"
},
+ "history": {
+ "title": "నోటిఫికేషన్ చరిత్ర",
+ "subtitle": "ఈ ఖాతా స్వీకరించిన నోటిఫికేషన్లు",
+ "bannerBody": "ఈ పరికరానికి పుష్ నోటిఫికేషన్లు ఆఫ్లో ఉన్నాయి, కాబట్టి కొత్తవి ఇక్కడికి రావు.",
+ "bannerAction": "నోటిఫికేషన్ సెట్టింగ్లు",
+ "emptyTitle": "ఇంకా నోటిఫికేషన్లు లేవు",
+ "emptyCategoryTitle": "ఈ వర్గంలో నోటిఫికేషన్లు లేవు",
+ "emptyBody": "ఈ ఖాతా స్వీకరించే నోటిఫికేషన్లు ఇక్కడ కనిపిస్తాయి. అవి {{days}} రోజులు ఉంచబడతాయి.",
+ "capped": "ఇటీవలి నోటిఫికేషన్లు చూపబడుతున్నాయి.",
+ "loadError": "నోటిఫికేషన్ చరిత్రను లోడ్ చేయలేకపోయాము.",
+ "openFailed": "ఈ నోటిఫికేషన్ ఇకపై ఎక్కడా తెరవదు."
+ },
"action": {
"openSession": "సెషన్ను తెరవండి"
},
diff --git a/apps/mobile/src/i18n/locales/th.json b/apps/mobile/src/i18n/locales/th.json
index 1a0b2c7ade3..f4410717a93 100644
--- a/apps/mobile/src/i18n/locales/th.json
+++ b/apps/mobile/src/i18n/locales/th.json
@@ -877,6 +877,18 @@
"spendAlertsSubtitle": "การเกินเกณฑ์และการพุ่งขึ้นรายชั่วโมง",
"spendAlertsToggle": "หมวดหมู่การแจ้งเตือนการใช้จ่าย"
},
+ "history": {
+ "title": "ประวัติการแจ้งเตือน",
+ "subtitle": "การแจ้งเตือนที่บัญชีนี้ได้รับ",
+ "bannerBody": "การแจ้งเตือนแบบพุชปิดอยู่สำหรับอุปกรณ์นี้ ดังนั้นการแจ้งเตือนใหม่จะไม่มาที่นี่",
+ "bannerAction": "การตั้งค่าการแจ้งเตือน",
+ "emptyTitle": "ยังไม่มีการแจ้งเตือน",
+ "emptyCategoryTitle": "ยังไม่มีการแจ้งเตือนในหมวดหมู่นี้",
+ "emptyBody": "การแจ้งเตือนที่บัญชีนี้ได้รับจะแสดงที่นี่ โดยจะเก็บไว้เป็นเวลา {{days}} วัน",
+ "capped": "กำลังแสดงการแจ้งเตือนล่าสุด",
+ "loadError": "ไม่สามารถโหลดประวัติการแจ้งเตือนได้",
+ "openFailed": "การแจ้งเตือนนี้ไม่สามารถเปิดได้อีกต่อไป"
+ },
"action": {
"openSession": "เปิดเซสชัน"
},
diff --git a/apps/mobile/src/i18n/locales/tr.json b/apps/mobile/src/i18n/locales/tr.json
index 6298a244f7a..f85c15f996f 100644
--- a/apps/mobile/src/i18n/locales/tr.json
+++ b/apps/mobile/src/i18n/locales/tr.json
@@ -353,6 +353,18 @@
"spendAlertsSubtitle": "eşik aşımları ve saatlik artışlar",
"spendAlertsToggle": "Harcama uyarıları kategorisi"
},
+ "history": {
+ "title": "Bildirim geçmişi",
+ "subtitle": "Bu hesabın aldığı bildirimler",
+ "bannerBody": "Bu cihazda anlık bildirimler kapalı, bu nedenle yenileri buraya gelmez.",
+ "bannerAction": "Bildirim ayarları",
+ "emptyTitle": "Henüz bildirim yok",
+ "emptyCategoryTitle": "Bu kategoride bildirim yok",
+ "emptyBody": "Bu hesabın aldığı bildirimler burada görünür. {{days}} gün boyunca saklanır.",
+ "capped": "En son bildirimler gösteriliyor.",
+ "loadError": "Bildirim geçmişi yüklenemedi.",
+ "openFailed": "Bu bildirim artık hiçbir yerde açılmıyor."
+ },
"action": {
"openSession": "Oturumu aç"
},
diff --git a/apps/mobile/src/i18n/locales/uk.json b/apps/mobile/src/i18n/locales/uk.json
index c70bebb29c6..4657d2488ac 100644
--- a/apps/mobile/src/i18n/locales/uk.json
+++ b/apps/mobile/src/i18n/locales/uk.json
@@ -353,6 +353,18 @@
"spendAlertsSubtitle": "перевищення порогів і погодинні сплески",
"spendAlertsToggle": "Категорія «Сповіщення про витрати»"
},
+ "history": {
+ "title": "Історія сповіщень",
+ "subtitle": "Сповіщення, які отримав цей акаунт",
+ "bannerBody": "Push-сповіщення вимкнено для цього пристрою, тому нові тут не надходитимуть.",
+ "bannerAction": "Налаштування сповіщень",
+ "emptyTitle": "Поки що немає сповіщень",
+ "emptyCategoryTitle": "У цій категорії немає сповіщень",
+ "emptyBody": "Сповіщення, які отримує цей акаунт, з'являються тут. Вони зберігаються протягом {{days}} днів.",
+ "capped": "Показано найновіші сповіщення.",
+ "loadError": "Не вдалося завантажити історію сповіщень.",
+ "openFailed": "Це сповіщення більше ніде не відкривається."
+ },
"action": {
"openSession": "Відкрити сеанс"
},
diff --git a/apps/mobile/src/i18n/locales/ur.json b/apps/mobile/src/i18n/locales/ur.json
index 786341c158e..388a913c521 100644
--- a/apps/mobile/src/i18n/locales/ur.json
+++ b/apps/mobile/src/i18n/locales/ur.json
@@ -877,6 +877,18 @@
"spendAlertsSubtitle": "حدوں سے تجاوز اور فی گھنٹہ اضافہ",
"spendAlertsToggle": "اخراجات کی تنبیہات کی قسم"
},
+ "history": {
+ "title": "اطلاعات کی سرگزشت",
+ "subtitle": "اس اکاؤنٹ کو موصول ہونے والی اطلاعات",
+ "bannerBody": "اس ڈیوائس کے لیے پش اطلاعات بند ہیں، اس لیے نئی اطلاعات یہاں موصول نہیں ہوں گی۔",
+ "bannerAction": "اطلاعات کی ترتیبات",
+ "emptyTitle": "ابھی تک کوئی اطلاعات نہیں",
+ "emptyCategoryTitle": "اس زمرے میں کوئی اطلاعات نہیں",
+ "emptyBody": "اس اکاؤنٹ کو موصول ہونے والی اطلاعات یہاں نظر آتی ہیں۔ یہ {{days}} دنوں تک محفوظ رکھی جاتی ہیں۔",
+ "capped": "حالیہ ترین اطلاعات دکھائی جا رہی ہیں۔",
+ "loadError": "اطلاعات کی سرگزشت لوڈ نہیں ہو سکی۔",
+ "openFailed": "یہ اطلاع اب کہیں نہیں کھلتی۔"
+ },
"action": {
"openSession": "سیشن کھولیں"
},
diff --git a/apps/mobile/src/i18n/locales/uz.json b/apps/mobile/src/i18n/locales/uz.json
index 1825ad552c9..20604da3250 100644
--- a/apps/mobile/src/i18n/locales/uz.json
+++ b/apps/mobile/src/i18n/locales/uz.json
@@ -877,6 +877,18 @@
"spendAlertsSubtitle": "chegaradan oshish va soatlik ko'tarilishlar",
"spendAlertsToggle": "Xarajat bildirishnomalari toifasi"
},
+ "history": {
+ "title": "Bildirishnomalar tarixi",
+ "subtitle": "Ushbu hisob olgan bildirishnomalar",
+ "bannerBody": "Ushbu qurilmada push-bildirishnomalar o'chirilgan, shuning uchun yangilari bu yerga kelmaydi.",
+ "bannerAction": "Bildirishnoma sozlamalari",
+ "emptyTitle": "Hozircha bildirishnomalar yo'q",
+ "emptyCategoryTitle": "Bu toifada bildirishnomalar yo'q",
+ "emptyBody": "Ushbu hisob oladigan bildirishnomalar shu yerda ko'rinadi. Ular {{days}} kun saqlanadi.",
+ "capped": "Eng so'nggi bildirishnomalar ko'rsatilmoqda.",
+ "loadError": "Bildirishnomalar tarixini yuklab bo'lmadi.",
+ "openFailed": "Bu bildirishnoma endi hech qayerda ochilmaydi."
+ },
"action": {
"openSession": "Sessiyani ochish"
},
diff --git a/apps/mobile/src/i18n/locales/vi.json b/apps/mobile/src/i18n/locales/vi.json
index 2dda5e179c1..6f13b8cd5d7 100644
--- a/apps/mobile/src/i18n/locales/vi.json
+++ b/apps/mobile/src/i18n/locales/vi.json
@@ -353,6 +353,18 @@
"spendAlertsSubtitle": "vượt ngưỡng và tăng vọt theo giờ",
"spendAlertsToggle": "Danh mục cảnh báo chi tiêu"
},
+ "history": {
+ "title": "Lịch sử thông báo",
+ "subtitle": "Các thông báo mà tài khoản này đã nhận",
+ "bannerBody": "Thông báo đẩy đã tắt trên thiết bị này, vì vậy thông báo mới sẽ không đến đây.",
+ "bannerAction": "Cài đặt thông báo",
+ "emptyTitle": "Chưa có thông báo nào",
+ "emptyCategoryTitle": "Không có thông báo nào trong danh mục này",
+ "emptyBody": "Các thông báo mà tài khoản này nhận được sẽ xuất hiện ở đây. Chúng được lưu trong {{days}} ngày.",
+ "capped": "Đang hiển thị các thông báo gần đây nhất.",
+ "loadError": "Không thể tải lịch sử thông báo.",
+ "openFailed": "Thông báo này không còn mở được ở đâu nữa."
+ },
"action": {
"openSession": "Mở phiên"
},
diff --git a/apps/mobile/src/i18n/locales/yo.json b/apps/mobile/src/i18n/locales/yo.json
index 5e8600f5acf..81117f60ed4 100644
--- a/apps/mobile/src/i18n/locales/yo.json
+++ b/apps/mobile/src/i18n/locales/yo.json
@@ -877,6 +877,18 @@
"spendAlertsSubtitle": "àwọn ìkọjá ààlà àti ìgbésókè lójú wákàtí",
"spendAlertsToggle": "Ẹ̀ka àwọn ìkìlọ̀ ìnáwó"
},
+ "history": {
+ "title": "Ìtàn àwọn ìfitónilétí",
+ "subtitle": "Àwọn ìfitónilétí tí àkáńtì yìí gbà",
+ "bannerBody": "Àwọn ìfitónilétí títì ti wà ní pípa fún ẹ̀rọ yìí, nítorí náà àwọn tuntun kò ní dé síbí.",
+ "bannerAction": "Àwọn ètò ìfitónilétí",
+ "emptyTitle": "Kò tíì sí ìfitónilétí",
+ "emptyCategoryTitle": "Kò sí ìfitónilétí kankan ní ẹ̀ka yìí",
+ "emptyBody": "Àwọn ìfitónilétí tí àkáńtì yìí ń gbà máa ń farahàn síbí. A máa ń tọ́jú wọn fún {{days}} ọjọ́.",
+ "capped": "Ń ṣàfihàn àwọn ìfitónilétí tuntun jùlọ.",
+ "loadError": "A kò lè gbé ìtàn àwọn ìfitónilétí wọ.",
+ "openFailed": "Ìfitónilétí yìí kò tún ṣí níbikíbi mọ́."
+ },
"action": {
"openSession": "Ṣí ìgbà iṣẹ́"
},
diff --git a/apps/mobile/src/i18n/locales/zh-Hans.json b/apps/mobile/src/i18n/locales/zh-Hans.json
index 32e8d0a4206..b36ab5b317b 100644
--- a/apps/mobile/src/i18n/locales/zh-Hans.json
+++ b/apps/mobile/src/i18n/locales/zh-Hans.json
@@ -353,6 +353,18 @@
"spendAlertsSubtitle": "阈值突破与每小时激增",
"spendAlertsToggle": "支出提醒类别"
},
+ "history": {
+ "title": "通知历史",
+ "subtitle": "此账户收到的通知",
+ "bannerBody": "此设备的推送通知已关闭,因此新通知不会在此处送达。",
+ "bannerAction": "通知设置",
+ "emptyTitle": "暂无通知",
+ "emptyCategoryTitle": "此类别中暂无通知",
+ "emptyBody": "此账户收到的通知会显示在这里。通知会保留 {{days}} 天。",
+ "capped": "仅显示最近的通知。",
+ "loadError": "无法加载通知历史。",
+ "openFailed": "此通知已无法在任何位置打开。"
+ },
"action": {
"openSession": "打开会话"
},
diff --git a/apps/mobile/src/i18n/locales/zh-Hant.json b/apps/mobile/src/i18n/locales/zh-Hant.json
index 48b1ec6b85f..8cf691441e1 100644
--- a/apps/mobile/src/i18n/locales/zh-Hant.json
+++ b/apps/mobile/src/i18n/locales/zh-Hant.json
@@ -353,6 +353,18 @@
"spendAlertsSubtitle": "門檻突破與每小時激增",
"spendAlertsToggle": "支出提醒類別"
},
+ "history": {
+ "title": "通知歷史",
+ "subtitle": "此帳戶收到的通知",
+ "bannerBody": "此裝置的推播通知已關閉,因此新通知不會送達這裡。",
+ "bannerAction": "通知設定",
+ "emptyTitle": "尚無通知",
+ "emptyCategoryTitle": "此類別尚無通知",
+ "emptyBody": "此帳戶收到的通知會顯示在這裡。通知會保留 {{days}} 天。",
+ "capped": "顯示最近的通知。",
+ "loadError": "無法載入通知歷史。",
+ "openFailed": "此通知已無法開啟。"
+ },
"action": {
"openSession": "開啟工作階段"
},
diff --git a/apps/mobile/src/i18n/locales/zu.json b/apps/mobile/src/i18n/locales/zu.json
index a782a7978bb..89b20461c86 100644
--- a/apps/mobile/src/i18n/locales/zu.json
+++ b/apps/mobile/src/i18n/locales/zu.json
@@ -877,6 +877,18 @@
"spendAlertsSubtitle": "ukweqa imingcele nokukhuphuka ngehora",
"spendAlertsToggle": "Isigaba sezexwayiso zokusebenzisa imali"
},
+ "history": {
+ "title": "Umlando wezaziso",
+ "subtitle": "Izaziso ezitholwe yile akhawunti",
+ "bannerBody": "Izaziso zokupush zivaliwe kule divayisi, ngakho ezintsha ngeke zifike lapha.",
+ "bannerAction": "Izilungiselelo zezaziso",
+ "emptyTitle": "Akukho izaziso okwamanje",
+ "emptyCategoryTitle": "Akukho izaziso kulesi sigaba",
+ "emptyBody": "Izaziso ezitholwa yile akhawunti zibonakala lapha. Zigcinwa izinsuku ezingama-{{days}}.",
+ "capped": "Kuboniswa izaziso zakamuva.",
+ "loadError": "Asikwazanga ukulayisha umlando wezaziso.",
+ "openFailed": "Lesi saziso asisavuleki ndawo."
+ },
"action": {
"openSession": "Vula iseshini"
},
diff --git a/apps/mobile/src/lib/hooks/use-notification-master-gate.ts b/apps/mobile/src/lib/hooks/use-notification-master-gate.ts
new file mode 100644
index 00000000000..cc0147f03d9
--- /dev/null
+++ b/apps/mobile/src/lib/hooks/use-notification-master-gate.ts
@@ -0,0 +1,74 @@
+import { useQuery } from '@tanstack/react-query';
+
+import { useAuth } from '@/lib/auth/auth-context';
+import { getDevicePushToken, getNotificationPermissionStatus } from '@/lib/notifications';
+import { useTRPC } from '@/lib/trpc';
+
+import { deriveGateSettled } from './agent-push-preference';
+
+/**
+ * The same cache keys the Notifications settings screen reads, so both surfaces
+ * share one permission read and one device-token read.
+ */
+const permissionQueryKey = ['notificationPermission'] as const;
+const deviceTokenQueryKey = ['devicePushToken'] as const;
+
+export type NotificationMasterGate = Readonly<{
+ /** No answer yet: do not render an enabled/disabled claim. */
+ loading: boolean;
+ /** Every input answered, including by failing. */
+ settled: boolean;
+ /** The OS allows this app to post notifications. */
+ permissionGranted: boolean;
+ /** OS permission granted and this device's token is registered on the account. */
+ pushEnabled: boolean;
+}>;
+
+/**
+ * Whether pushes can reach this device: the OS permission plus a token the
+ * account holds. The settings screen's master switch is the same pair, so a
+ * screen that says "push is off" and the switch that turns it back on cannot
+ * disagree.
+ *
+ * A permission read that fails counts as not granted, which is what
+ * `deriveGateSettled` short-circuits on: the token queries are irrelevant to a
+ * closed gate, and the device-token query is disabled while it is closed.
+ */
+export function useNotificationMasterGate(): NotificationMasterGate {
+ const trpc = useTRPC();
+ const { token: authToken } = useAuth();
+
+ const permission = useQuery({
+ queryKey: permissionQueryKey,
+ queryFn: async () => (await getNotificationPermissionStatus()) === 'granted',
+ });
+ const permissionGranted = permission.data === true;
+
+ const deviceToken = useQuery({
+ queryKey: deviceTokenQueryKey,
+ queryFn: getDevicePushToken,
+ enabled: permissionGranted,
+ });
+ const pushTokens = useQuery({
+ ...trpc.user.getMyPushTokens.queryOptions(),
+ enabled: authToken != null,
+ });
+
+ const settled = deriveGateSettled({
+ permissionSettled: permission.isFetched || permission.isError,
+ permissionGranted,
+ pushTokensSettled: pushTokens.isFetched || pushTokens.isError,
+ deviceTokenSettled: deviceToken.isFetched || deviceToken.isError,
+ });
+ const registeredToken = deviceToken.data ?? null;
+ const serverRegistered =
+ registeredToken !== null &&
+ (pushTokens.data ?? []).some(entry => entry.token === registeredToken);
+
+ return {
+ loading: !settled,
+ settled,
+ permissionGranted,
+ pushEnabled: permissionGranted && serverRegistered,
+ };
+}
diff --git a/apps/mobile/src/lib/notification-history.test.ts b/apps/mobile/src/lib/notification-history.test.ts
new file mode 100644
index 00000000000..b8955ffada5
--- /dev/null
+++ b/apps/mobile/src/lib/notification-history.test.ts
@@ -0,0 +1,154 @@
+import { describe, expect, it } from 'vitest';
+
+import {
+ NOTIFICATION_HISTORY_FILTERS,
+ notificationHistoryCopy,
+ notificationHistoryFilters,
+ notificationHistoryTarget,
+} from '@/lib/notification-history';
+
+const LOW_BALANCE_PARAMS = { organizationName: 'Acme', minimumBalanceUsd: '5' };
+
+describe('notificationHistoryCopy', () => {
+ it('renders the stored catalog key and its params in the requested language', () => {
+ const copy = notificationHistoryCopy(
+ {
+ i18nKey: 'internal.lowBalance',
+ i18nParams: LOW_BALANCE_PARAMS,
+ title: 'stored title',
+ body: 'stored body',
+ },
+ 'de'
+ );
+
+ expect(copy.title).toBe('Warnung bei niedrigem Guthaben');
+ expect(copy.body).toBe('Das Guthaben von Acme fiel unter $5');
+ });
+
+ it('renders English copy for a locale the push catalog does not carry', () => {
+ const copy = notificationHistoryCopy(
+ { i18nKey: 'internal.lowBalance', i18nParams: LOW_BALANCE_PARAMS, title: '', body: '' },
+ 'not-a-locale'
+ );
+
+ expect(copy).toEqual({
+ title: 'Low balance alert',
+ body: 'Acme balance fell below $5',
+ });
+ });
+
+ it('falls back to the stored copy when the catalog key no longer exists', () => {
+ const copy = notificationHistoryCopy(
+ {
+ i18nKey: 'internal.retiredKind',
+ i18nParams: { anything: 'x' },
+ title: 'Stored title',
+ body: 'Stored body',
+ },
+ 'de'
+ );
+
+ expect(copy).toEqual({ title: 'Stored title', body: 'Stored body' });
+ });
+
+ it('uses the stored copy for a push that carried no catalog key', () => {
+ const copy = notificationHistoryCopy(
+ { i18nKey: null, i18nParams: null, title: 'Raw title', body: 'Raw body' },
+ 'de'
+ );
+
+ expect(copy).toEqual({ title: 'Raw title', body: 'Raw body' });
+ });
+});
+
+describe('notificationHistoryTarget', () => {
+ it.each([
+ [
+ { type: 'cloud_agent_session', cliSessionId: 'ses_1', organizationId: 'org-2' },
+ '/(app)/agent-chat/ses_1',
+ ],
+ [{ type: 'agent_briefing', url: '/agent-briefing' }, '/(app)/agent-briefing'],
+ ])('opens the push destination of a %o row without the push marker', (payload, href) => {
+ expect(notificationHistoryTarget(payload)).toMatchObject({ href });
+ });
+
+ it('keeps the other query params and drops only the push marker', () => {
+ expect(
+ notificationHistoryTarget({ type: 'low_balance', organizationId: 'org-acme' })
+ ).toMatchObject({
+ href: '/(app)/(tabs)/(3_profile)/organization/credit-activity?org=org-acme',
+ });
+ expect(
+ notificationHistoryTarget({
+ type: 'spend_alert',
+ scope: 'organization',
+ organizationId: 'org-acme',
+ })
+ ).toMatchObject({
+ href: '/(app)/(tabs)/(3_profile)/spend-alerts?org=org-acme',
+ });
+ });
+
+ it('routes a security finding row to its scope', () => {
+ expect(
+ notificationHistoryTarget({
+ type: 'security_finding',
+ findingId: 'f-1',
+ scope: 'org-9',
+ })
+ ).toMatchObject({ href: '/(app)/security-agent/org-9/findings/f-1' });
+ });
+
+ it('carries the payload the row was recorded from', () => {
+ const target = notificationHistoryTarget({
+ type: 'cloud_agent_session',
+ cliSessionId: 'ses_1',
+ organizationId: 'org-2',
+ });
+
+ expect(target?.data).toEqual({
+ type: 'cloud_agent_session',
+ cliSessionId: 'ses_1',
+ organizationId: 'org-2',
+ });
+ });
+
+ it('reports no destination for a payload this app cannot parse', () => {
+ expect(notificationHistoryTarget({ type: 'retired_kind' })).toBeNull();
+ expect(notificationHistoryTarget({})).toBeNull();
+ });
+});
+
+describe('notificationHistoryFilters', () => {
+ it('offers every recorded category behind an "All" chip', () => {
+ expect(NOTIFICATION_HISTORY_FILTERS.map(filter => filter.category)).toEqual([
+ null,
+ 'chatMessages',
+ 'agentAttention',
+ 'sessionStatus',
+ 'balanceAlerts',
+ 'spendAlerts',
+ 'securityFindings',
+ ]);
+ });
+
+ it('drops a category the account cannot receive', () => {
+ const filters = notificationHistoryFilters({
+ balanceAlerts: { available: false },
+ securityFindings: { available: false },
+ });
+
+ expect(filters.map(filter => filter.category)).toEqual([
+ null,
+ 'chatMessages',
+ 'agentAttention',
+ 'sessionStatus',
+ 'spendAlerts',
+ ]);
+ });
+
+ it('keeps every chip while availability is unknown or unread', () => {
+ expect(notificationHistoryFilters(undefined)).toEqual(NOTIFICATION_HISTORY_FILTERS);
+ expect(notificationHistoryFilters({})).toEqual(NOTIFICATION_HISTORY_FILTERS);
+ });
+});
diff --git a/apps/mobile/src/lib/notification-history.ts b/apps/mobile/src/lib/notification-history.ts
new file mode 100644
index 00000000000..89f7c23012a
--- /dev/null
+++ b/apps/mobile/src/lib/notification-history.ts
@@ -0,0 +1,143 @@
+import {
+ NOTIFICATION_HISTORY_CATEGORIES,
+ type NotificationHistoryCategory,
+ type PushData,
+ pushDataSchema,
+ translatePush,
+} from '@kilocode/notifications';
+
+import { notificationPathForData } from './notification-path';
+
+/**
+ * Pure logic for the Notification history screen: how a recorded row is read
+ * back, where a row's tap goes, and which filter chips the list offers.
+ *
+ * The row is rendered on the device from the catalog key and params the push
+ * carried, in the app's current language, so a row reads like the notification
+ * did. The worker's own renderer does the same with its per-token locale
+ * (`NotificationChannelDO.dispatchPush`); the stored fallback copy is what a
+ * removed key falls back to.
+ */
+
+/** The stored fields a row's copy is rendered from. */
+export type NotificationHistoryCopyFields = Readonly<{
+ /** Catalog key without a `.title`/`.body` suffix, or null for a raw-form push. */
+ i18nKey: string | null;
+ i18nParams: Record | null;
+ /** The copy the sender stored, used verbatim when no key resolves. */
+ title: string;
+ body: string;
+}>;
+
+/** The rendered title and body of one row. */
+export type NotificationHistoryCopy = Readonly<{ title: string; body: string }>;
+
+/**
+ * The row's title and body in `locale`. A row with no catalog key, or whose key
+ * is no longer in the push catalog, keeps the stored fallback copy — never the
+ * content-free preview copy, which is a per-device send-time setting and is
+ * never what was recorded.
+ */
+export function notificationHistoryCopy(
+ row: NotificationHistoryCopyFields,
+ locale: string
+): NotificationHistoryCopy {
+ if (row.i18nKey === null) {
+ return { title: row.title, body: row.body };
+ }
+ const params = row.i18nParams ?? undefined;
+ return {
+ title: translatePush(locale, `${row.i18nKey}.title`, params, row.title),
+ body: translatePush(locale, `${row.i18nKey}.body`, params, row.body),
+ };
+}
+
+export type NotificationHistoryTarget = Readonly<{
+ data: PushData;
+ href: string;
+}>;
+
+/**
+ * Drop the `via=push` marker from a destination. The marker tags the landing
+ * screen's analytics event as push-originated, and opening a row from the list
+ * is not a push tap.
+ */
+function withoutPushOrigin(href: string): string {
+ const queryStart = href.indexOf('?');
+ if (queryStart === -1) {
+ return href;
+ }
+ const kept = href
+ .slice(queryStart + 1)
+ .split('&')
+ .filter(part => part !== 'via=push');
+ if (kept.length === 0) {
+ return href.slice(0, queryStart);
+ }
+ return `${href.slice(0, queryStart)}?${kept.join('&')}`;
+}
+
+/**
+ * Where a row's tap goes, or null when the stored payload no longer resolves to
+ * a destination (a producer shape this app cannot parse). A null target is the
+ * one dead destination the list can detect without a request: every recorded
+ * kind maps to a route.
+ */
+export function notificationHistoryTarget(
+ payload: Record
+): NotificationHistoryTarget | null {
+ const parsed = pushDataSchema.safeParse(payload);
+ if (!parsed.success) {
+ return null;
+ }
+ return { data: parsed.data, href: withoutPushOrigin(notificationPathForData(parsed.data)) };
+}
+
+export type NotificationHistoryFilter = Readonly<{
+ /** null is the unfiltered "All" chip. */
+ category: NotificationHistoryCategory | null;
+ labelKey: string;
+}>;
+
+/**
+ * Chip label per recorded category. The settings screen's own wording, so a
+ * chip and the switch that governs it read the same. Keys are literal so the
+ * catalog check can see them.
+ */
+const CATEGORY_LABEL_KEYS = {
+ chatMessages: 'notifications.channel.chat',
+ agentAttention: 'notifications.category.agentAttentionTitle',
+ sessionStatus: 'notifications.category.sessionStatusTitle',
+ balanceAlerts: 'notifications.channel.balance',
+ spendAlerts: 'notifications.channel.spend',
+ securityFindings: 'notifications.channel.security',
+} satisfies Record;
+
+/**
+ * Every chip in the order the settings screen lists the categories: "All"
+ * first, then one per category the recorder can produce. A category the
+ * recorder never writes (there is no recorded push kind for it) has no chip,
+ * so no chip can only ever answer an empty page.
+ */
+export const NOTIFICATION_HISTORY_FILTERS: readonly NotificationHistoryFilter[] = [
+ { category: null, labelKey: 'common.all' },
+ ...NOTIFICATION_HISTORY_CATEGORIES.map(category => ({
+ category,
+ labelKey: CATEGORY_LABEL_KEYS[category],
+ })),
+];
+
+/**
+ * The chips an account can use: a category the server marks unavailable (no
+ * organization, no security agent) is dropped rather than offered as a control
+ * that can only return an empty page. Availability the client has not read yet
+ * — or could not read — keeps the chip, so a failed preferences query never
+ * hides a filter the account does have.
+ */
+export function notificationHistoryFilters(
+ capabilities: Readonly> | undefined
+): readonly NotificationHistoryFilter[] {
+ return NOTIFICATION_HISTORY_FILTERS.filter(
+ filter => filter.category === null || capabilities?.[filter.category]?.available !== false
+ );
+}
diff --git a/packages/notifications/src/session-live-activity.ts b/packages/notifications/src/session-live-activity.ts
index b668b8648c5..7dc40bcda6a 100644
--- a/packages/notifications/src/session-live-activity.ts
+++ b/packages/notifications/src/session-live-activity.ts
@@ -149,7 +149,6 @@ export function buildSessionLiveActivityContentState(
*/
export function sessionCardStaleAtSeconds(
content: Pick,
- nowSeconds: number,
defaultStaleAtSeconds: number
): number {
if (content.statusKind !== 'running') return defaultStaleAtSeconds;
diff --git a/packages/trpc/package.json b/packages/trpc/package.json
index 8ebdefb6380..31b882c3d81 100644
--- a/packages/trpc/package.json
+++ b/packages/trpc/package.json
@@ -20,6 +20,7 @@
"@kilocode/kilo-chat": "workspace:*",
"@kilocode/kiloclaw-instance-tiers": "workspace:*",
"@kilocode/kiloclaw-secret-catalog": "workspace:*",
+ "@kilocode/notifications": "workspace:*",
"@kilocode/worker-utils": "workspace:*",
"@trpc/server": "catalog:",
"@workos-inc/node": "catalog:",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index ad870d29f74..ff6e930b1c1 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -1910,6 +1910,9 @@ importers:
'@kilocode/kiloclaw-secret-catalog':
specifier: workspace:*
version: link:../kiloclaw-secret-catalog
+ '@kilocode/notifications':
+ specifier: workspace:*
+ version: link:../notifications
'@kilocode/worker-utils':
specifier: workspace:*
version: link:../worker-utils
diff --git a/services/notifications/src/lib/session-card-delivery.ts b/services/notifications/src/lib/session-card-delivery.ts
index 022ad19c09a..9242134ef9b 100644
--- a/services/notifications/src/lib/session-card-delivery.ts
+++ b/services/notifications/src/lib/session-card-delivery.ts
@@ -278,7 +278,6 @@ export function planSessionCardSend(params: {
// The quiet window for a working card, the aggregate window otherwise.
staleAtSeconds: sessionCardStaleAtSeconds(
content,
- nowSeconds,
nowSeconds + SESSION_CARD_AGGREGATE_STALE_MS / 1000
),
},