diff --git a/app/actions/app/global.ts b/app/actions/app/global.ts index 3de806fb8..fde5c8d11 100644 --- a/app/actions/app/global.ts +++ b/app/actions/app/global.ts @@ -77,3 +77,11 @@ export const removeLastViewedChannelIdAndServer = async () => { export const removeLastViewedThreadIdAndServer = async () => { return storeGlobal(GLOBAL_IDENTIFIERS.LAST_VIEWED_THREAD, null, false); }; + +export const storePushDisabledInServerAcknowledged = async (serverUrl: string) => { + return storeGlobal(`${GLOBAL_IDENTIFIERS.PUSH_DISABLED_ACK}${serverUrl}`, 'true', false); +}; + +export const removePushDisabledInServerAcknowledged = async (serverUrl: string) => { + return storeGlobal(`${GLOBAL_IDENTIFIERS.PUSH_DISABLED_ACK}${serverUrl}`, null, false); +}; diff --git a/app/constants/database.ts b/app/constants/database.ts index f14d22a62..d302c280a 100644 --- a/app/constants/database.ts +++ b/app/constants/database.ts @@ -82,6 +82,7 @@ export const GLOBAL_IDENTIFIERS = { ONBOARDING: 'onboarding', LAST_VIEWED_CHANNEL: 'lastViewedChannel', LAST_VIEWED_THREAD: 'lastViewedThread', + PUSH_DISABLED_ACK: 'pushDisabledAck', }; export enum OperationType { diff --git a/app/managers/session_manager.ts b/app/managers/session_manager.ts index 0e21ca4a4..1ba1fbbb0 100644 --- a/app/managers/session_manager.ts +++ b/app/managers/session_manager.ts @@ -5,7 +5,7 @@ import CookieManager, {type Cookie} from '@react-native-cookies/cookies'; import {AppState, type AppStateStatus, DeviceEventEmitter, Platform} from 'react-native'; import FastImage from 'react-native-fast-image'; -import {storeOnboardingViewedValue} from '@actions/app/global'; +import {removePushDisabledInServerAcknowledged, storeOnboardingViewedValue} from '@actions/app/global'; import {cancelSessionNotification, logout, scheduleSessionNotification} from '@actions/remote/session'; import {Events, Launch} from '@constants'; import DatabaseManager from '@database/manager'; @@ -22,6 +22,7 @@ import {getThemeFromState} from '@screens/navigation'; import EphemeralStore from '@store/ephemeral_store'; import {deleteFileCache, deleteFileCacheByDir} from '@utils/file'; import {isMainActivity} from '@utils/helpers'; +import {urlSafeBase64Encode} from '@utils/security'; import {addNewServer} from '@utils/server'; import type {LaunchType} from '@typings/launch'; @@ -121,6 +122,7 @@ class SessionManager { WebsocketManager.invalidateClient(serverUrl); if (removeServer) { + await removePushDisabledInServerAcknowledged(urlSafeBase64Encode(serverUrl)); await DatabaseManager.destroyServerDatabase(serverUrl); } else { await DatabaseManager.deleteServerDatabase(serverUrl); diff --git a/app/queries/app/global.ts b/app/queries/app/global.ts index 9784c6124..e1ff99bb8 100644 --- a/app/queries/app/global.ts +++ b/app/queries/app/global.ts @@ -55,6 +55,22 @@ export const getDontAskForReview = async () => { return Boolean(records?.[0]?.value); }; +export const getPushDisabledInServerAcknowledged = async (serverDomainString: string) => { + const records = await queryGlobalValue(`${GLOBAL_IDENTIFIERS.PUSH_DISABLED_ACK}${serverDomainString}`)?.fetch(); + return Boolean(records?.[0]?.value); +}; + +export const observePushDisabledInServerAcknowledged = (serverDomainString: string) => { + const query = queryGlobalValue(`${GLOBAL_IDENTIFIERS.PUSH_DISABLED_ACK}${serverDomainString}`); + if (!query) { + return of$(false); + } + return query.observe().pipe( + switchMap((result) => (result.length ? result[0].observe() : of$(false))), + switchMap((v) => of$(Boolean(v))), + ); +}; + export const getFirstLaunch = async () => { const records = await queryGlobalValue(GLOBAL_IDENTIFIERS.FIRST_LAUNCH)?.fetch(); if (!records?.[0]?.value) { diff --git a/app/screens/home/channel_list/categories_list/header/header.test.tsx b/app/screens/home/channel_list/categories_list/header/header.test.tsx index 2832e7f3d..7299f0c98 100644 --- a/app/screens/home/channel_list/categories_list/header/header.test.tsx +++ b/app/screens/home/channel_list/categories_list/header/header.test.tsx @@ -3,7 +3,7 @@ import React from 'react'; -import {PUSH_PROXY_STATUS_VERIFIED} from '@constants/push_proxy'; +import {PUSH_PROXY_RESPONSE_NOT_AVAILABLE, PUSH_PROXY_STATUS_VERIFIED} from '@constants/push_proxy'; import {renderWithIntl} from '@test/intl-test-helper'; import Header from './header'; @@ -22,4 +22,18 @@ describe('components/channel_list/header', () => { expect(toJSON()).toMatchSnapshot(); }); + + it('Push notifications disabled show alert icon', () => { + const wrapper = renderWithIntl( +
, + ); + + expect(wrapper.getByTestId('channel_list_header.push_alert')).toBeTruthy(); + }); }); diff --git a/app/screens/home/channel_list/categories_list/header/header.tsx b/app/screens/home/channel_list/categories_list/header/header.tsx index 4cec3fde3..ca2840642 100644 --- a/app/screens/home/channel_list/categories_list/header/header.tsx +++ b/app/screens/home/channel_list/categories_list/header/header.tsx @@ -206,7 +206,7 @@ const ChannelListHeader = ({ > {serverDisplayName} - {(pushProxyStatus !== PUSH_PROXY_STATUS_VERIFIED) && ( + {pushProxyStatus !== PUSH_PROXY_STATUS_VERIFIED && ( { + if (buttonIndex === 0 && serverUrl) { + // User clicked "Okay" acknowledging that the push notifications are disabled on that server + await storePushDisabledInServerAcknowledged(urlSafeBase64Encode(serverUrl)); + } +}; + +export function alertPushProxyError(intl: IntlShape, serverUrl?: string) { Alert.alert( intl.formatMessage({ id: 'alert.push_proxy_error.title', @@ -31,10 +51,11 @@ export function alertPushProxyError(intl: IntlShape) { }), intl.formatMessage({ id: 'alert.push_proxy_error.description', - defaultMessage: 'Due to the configuration for this server, notifications cannot be received in the mobile app. Contact your system admin for more information.', + defaultMessage: 'Due to the configuration of this server, notifications cannot be received in the mobile app. Contact your system admin for more information.', }), [{ text: intl.formatMessage({id: 'alert.push_proxy.button', defaultMessage: 'Okay'}), + onPress: () => handleAlertResponse(0, serverUrl), }], ); } diff --git a/assets/base/i18n/en.json b/assets/base/i18n/en.json index 6c656d9bd..0f93b5a28 100644 --- a/assets/base/i18n/en.json +++ b/assets/base/i18n/en.json @@ -15,7 +15,7 @@ "account.your_profile": "Your Profile", "alert.channel_deleted.description": "The channel {displayName} has been archived.", "alert.channel_deleted.title": "Archived channel", - "alert.push_proxy_error.description": "Due to the configuration for this server, notifications cannot be received in the mobile app. Contact your system admin for more information.", + "alert.push_proxy_error.description": "Due to the configuration of this server, notifications cannot be received in the mobile app. Contact your system admin for more information.", "alert.push_proxy_error.title": "Notifications cannot be received from this server", "alert.push_proxy_unknown.description": "This server was unable to receive push notifications for an unknown reason. This will be attempted again next time you connect.", "alert.push_proxy_unknown.title": "Notifications could not be received from this server", diff --git a/assets/base/i18n/en_AU.json b/assets/base/i18n/en_AU.json index e6dbf6303..b0f133bc0 100644 --- a/assets/base/i18n/en_AU.json +++ b/assets/base/i18n/en_AU.json @@ -686,7 +686,7 @@ "alert.push_proxy.button": "Okay", "alert.push_proxy_unknown.title": "Notifications could not be received from this server", "alert.push_proxy_error.title": "Notifications cannot be received from this server", - "alert.push_proxy_error.description": "Due to the configuration for this server, notifications cannot be received in the mobile app. Contact your system admin for more information.", + "alert.push_proxy_error.description": "Due to the configuration of this server, notifications cannot be received in the mobile app. Contact your system admin for more information.", "alert.channel_deleted.title": "Archived channel", "alert.channel_deleted.description": "The channel {displayName} has been archived.", "account.your_profile": "Your Profile",