From 0da1b5c6a434a71e78181e89f23e90d686798d2c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Andr=C3=A9s=20V=C3=A9lez=20Vidal?= Date: Wed, 9 Aug 2023 19:52:37 +0200 Subject: [PATCH 1/7] MM-50354 - do not show push disabled notification once acknowledged --- app/actions/app/global.ts | 8 ++++ app/constants/database.ts | 1 + app/managers/session_manager.ts | 6 ++- app/queries/app/global.ts | 16 +++++++ .../categories_list/header/header.test.tsx | 1 + .../categories_list/header/header.tsx | 6 ++- .../categories_list/header/index.ts | 12 +++++- .../servers/servers_list/server_item/index.ts | 4 +- .../servers_list/server_item/server_item.tsx | 4 +- app/utils/helpers.ts | 42 +++++++++++++++++++ app/utils/push_proxy.ts | 28 +++++++++++-- 11 files changed, 117 insertions(+), 11 deletions(-) diff --git a/app/actions/app/global.ts b/app/actions/app/global.ts index bb98cd5db..c258ba8a3 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..c79a69cff 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'; @@ -21,7 +21,7 @@ import {getCurrentUser} from '@queries/servers/user'; import {getThemeFromState} from '@screens/navigation'; import EphemeralStore from '@store/ephemeral_store'; import {deleteFileCache, deleteFileCacheByDir} from '@utils/file'; -import {isMainActivity} from '@utils/helpers'; +import {extractCleanDomain, isMainActivity} from '@utils/helpers'; import {addNewServer} from '@utils/server'; import type {LaunchType} from '@typings/launch'; @@ -168,6 +168,8 @@ class SessionManager { await this.terminateSession(serverUrl, removeServer); + await removePushDisabledInServerAcknowledged(extractCleanDomain(serverUrl)); + if (activeServerUrl === serverUrl) { let displayName = ''; let launchType: LaunchType = Launch.AddServer; 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..af40f111a 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 @@ -17,6 +17,7 @@ describe('components/channel_list/header', () => { canJoinChannels={true} canInvitePeople={true} displayName={'Test!'} + pushDisabledAck={true} />, ); 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..fb1528d9b 100644 --- a/app/screens/home/channel_list/categories_list/header/header.tsx +++ b/app/screens/home/channel_list/categories_list/header/header.tsx @@ -38,6 +38,7 @@ type Props = { iconPad?: boolean; onHeaderPress?: () => void; pushProxyStatus: string; + pushDisabledAck: boolean; } const getStyles = makeStyleSheetFromTheme((theme: Theme) => ({ @@ -111,6 +112,7 @@ const ChannelListHeader = ({ iconPad, onHeaderPress, pushProxyStatus, + pushDisabledAck, }: Props) => { const theme = useTheme(); const isTablet = useIsTablet(); @@ -166,7 +168,7 @@ const ChannelListHeader = ({ const onPushAlertPress = useCallback(() => { if (pushProxyStatus === PUSH_PROXY_STATUS_NOT_AVAILABLE) { - alertPushProxyError(intl); + alertPushProxyError(intl, serverUrl); } else { alertPushProxyUnknown(intl); } @@ -206,7 +208,7 @@ const ChannelListHeader = ({ > {serverDisplayName} - {(pushProxyStatus !== PUSH_PROXY_STATUS_VERIFIED) && ( + {pushProxyStatus !== PUSH_PROXY_STATUS_VERIFIED && !pushDisabledAck && ( { +type Props = WithDatabaseArgs & { + serverUrl: string; +} + +const enhanced = withObservables([], ({serverUrl, database}: Props) => { const team = observeCurrentTeam(database); const currentUser = observeCurrentUser(database); @@ -57,7 +64,8 @@ const enhanced = withObservables([], ({database}: WithDatabaseArgs) => { distinctUntilChanged(), ), pushProxyStatus: observePushVerificationStatus(database), + pushDisabledAck: observePushDisabledInServerAcknowledged(extractCleanDomain(serverUrl)), }; }); -export default withDatabase(enhanced(ChannelListHeader)); +export default withDatabase(withServerUrl(enhanced(ChannelListHeader))); diff --git a/app/screens/home/channel_list/servers/servers_list/server_item/index.ts b/app/screens/home/channel_list/servers/servers_list/server_item/index.ts index a46828e71..626f5121d 100644 --- a/app/screens/home/channel_list/servers/servers_list/server_item/index.ts +++ b/app/screens/home/channel_list/servers/servers_list/server_item/index.ts @@ -4,10 +4,11 @@ import withObservables from '@nozbe/with-observables'; import {of as of$} from 'rxjs'; +import {extractCleanDomain} from '@app/utils/helpers'; import {Tutorial} from '@constants'; import {PUSH_PROXY_STATUS_UNKNOWN} from '@constants/push_proxy'; import DatabaseManager from '@database/manager'; -import {observeTutorialWatched} from '@queries/app/global'; +import {observePushDisabledInServerAcknowledged, observeTutorialWatched} from '@queries/app/global'; import {observePushVerificationStatus} from '@queries/servers/system'; import ServerItem from './server_item'; @@ -26,6 +27,7 @@ const enhance = withObservables(['highlight'], ({highlight, server}: {highlight: server: server.observe(), tutorialWatched, pushProxyStatus: serverDatabase ? observePushVerificationStatus(serverDatabase) : of$(PUSH_PROXY_STATUS_UNKNOWN), + pushDisabledAck: observePushDisabledInServerAcknowledged(extractCleanDomain(server.url)), }; }); diff --git a/app/screens/home/channel_list/servers/servers_list/server_item/server_item.tsx b/app/screens/home/channel_list/servers/servers_list/server_item/server_item.tsx index efe10083e..0f463cba6 100644 --- a/app/screens/home/channel_list/servers/servers_list/server_item/server_item.tsx +++ b/app/screens/home/channel_list/servers/servers_list/server_item/server_item.tsx @@ -44,6 +44,7 @@ type Props = { server: ServersModel; tutorialWatched: boolean; pushProxyStatus: string; + pushDisabledAck: boolean; } type BadgeValues = { @@ -147,6 +148,7 @@ const ServerItem = ({ server, tutorialWatched, pushProxyStatus, + pushDisabledAck, }: Props) => { const intl = useIntl(); const theme = useTheme(); @@ -428,7 +430,7 @@ const ServerItem = ({ > {displayName} - {server.lastActiveAt > 0 && pushProxyStatus !== PUSH_PROXY_STATUS_VERIFIED && ( + {server.lastActiveAt > 0 && pushProxyStatus !== PUSH_PROXY_STATUS_VERIFIED && !pushDisabledAck && ( Result: example-something + * // Input: http://192.168.1.1 => Result: 192.168.1.1 + * // Input: https://127.0.0.1:3000 => Result: 127.0.0.1 + */ +export function extractDomain(input: string) { + const regex = /^(https?:\/\/)?([\w.-]+)(:\d+)?$/; + const match = input.match(regex); + + if (match && match[2]) { + return match[2].replace(/[\W.]/g, ''); + } + + return ''; +} + +/** + * Returns the domain name or IP address from a serverUrl without special characters. + * + * @param {string} domain - The extracted domain from a serverUrl. + * @returns {string} The extracted domain name or IP address, modified to remove special characters, + * or empty . + * + * // Output: + * // Input: example-something => Result: examplesomething + * // Input: 192.168.1.1 => Result: 19216811 + * // Input: 127.0.0.1 => Result: 127001 + */ +export function extractCleanDomain(domain: string) { + if (!domain) { + return ''; + } + return extractDomain(domain).replace(/[\W.]/g, ''); +} diff --git a/app/utils/push_proxy.ts b/app/utils/push_proxy.ts index e207c9841..7bfba76c2 100644 --- a/app/utils/push_proxy.ts +++ b/app/utils/push_proxy.ts @@ -3,16 +3,30 @@ import {Alert} from 'react-native'; +import {storePushDisabledInServerAcknowledged} from '@actions/app/global'; +import {getPushDisabledInServerAcknowledged} from '@app/queries/app/global'; import {PUSH_PROXY_RESPONSE_NOT_AVAILABLE, PUSH_PROXY_RESPONSE_UNKNOWN, PUSH_PROXY_STATUS_NOT_AVAILABLE, PUSH_PROXY_STATUS_UNKNOWN, PUSH_PROXY_STATUS_VERIFIED} from '@constants/push_proxy'; import EphemeralStore from '@store/ephemeral_store'; +import {extractCleanDomain} from './helpers'; + import type {IntlShape} from 'react-intl'; -export function canReceiveNotifications(serverUrl: string, verification: string, intl: IntlShape) { +export async function pushDisabledInServerAck(serverUrl: string) { + const extractedDomain = extractCleanDomain(serverUrl); + const pushServerDisabledAck = await getPushDisabledInServerAcknowledged(extractedDomain); + return pushServerDisabledAck; +} + +export async function canReceiveNotifications(serverUrl: string, verification: string, intl: IntlShape) { + const a = await pushDisabledInServerAck(serverUrl); + switch (verification) { case PUSH_PROXY_RESPONSE_NOT_AVAILABLE: EphemeralStore.setPushProxyVerificationState(serverUrl, PUSH_PROXY_STATUS_NOT_AVAILABLE); - alertPushProxyError(intl); + if (!a) { + alertPushProxyError(intl, serverUrl); + } break; case PUSH_PROXY_RESPONSE_UNKNOWN: EphemeralStore.setPushProxyVerificationState(serverUrl, PUSH_PROXY_STATUS_UNKNOWN); @@ -23,7 +37,14 @@ export function canReceiveNotifications(serverUrl: string, verification: string, } } -export function alertPushProxyError(intl: IntlShape) { +const handleAlertResponse = async (buttonIndex: number, serverUrl: string) => { + if (buttonIndex === 0) { + // User clicked "Okay" acknowledging that the push notifications are disabled on that server + await storePushDisabledInServerAcknowledged(extractCleanDomain(serverUrl)); + } +}; + +export function alertPushProxyError(intl: IntlShape, serverUrl: string) { Alert.alert( intl.formatMessage({ id: 'alert.push_proxy_error.title', @@ -35,6 +56,7 @@ export function alertPushProxyError(intl: IntlShape) { }), [{ text: intl.formatMessage({id: 'alert.push_proxy.button', defaultMessage: 'Okay'}), + onPress: () => handleAlertResponse(0, serverUrl), }], ); } From 7d3115c92043a75b7d9a759cb2b9e3e20232a7c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Andr=C3=A9s=20V=C3=A9lez=20Vidal?= Date: Thu, 10 Aug 2023 13:53:27 +0200 Subject: [PATCH 2/7] enhance regular expression to support optional subpath --- app/utils/helpers.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/app/utils/helpers.ts b/app/utils/helpers.ts index d464492ff..715951d38 100644 --- a/app/utils/helpers.ts +++ b/app/utils/helpers.ts @@ -174,13 +174,16 @@ export function isMainActivity() { * // Input: https://example-something.com => Result: example-something * // Input: http://192.168.1.1 => Result: 192.168.1.1 * // Input: https://127.0.0.1:3000 => Result: 127.0.0.1 + * // Input: https://subdomain.example.com/api => subdomain.example.com/api + * // Input: http://localhost:8080/app/v1 => localhost:8080/app/v1 */ export function extractDomain(input: string) { - const regex = /^(https?:\/\/)?([\w.-]+)(:\d+)?$/; + const regex = /^(https?:\/\/)?([\w.-]+)(:\d+)?(\/\S*)?/; const match = input.match(regex); - if (match && match[2]) { - return match[2].replace(/[\W.]/g, ''); + if (match) { + const modifiedString = match[2] + (match[4] || ''); + return modifiedString; } return ''; From 7244f95fea86ebd3866a68edb8cc36d871738b79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Andr=C3=A9s=20V=C3=A9lez=20Vidal?= Date: Mon, 21 Aug 2023 17:31:16 +0200 Subject: [PATCH 3/7] add unit test; refactor function; not remove on logout but on server remove --- app/managers/session_manager.ts | 5 +-- .../categories_list/header/header.test.tsx | 32 +++++++++++++++- .../categories_list/header/index.ts | 4 +- .../servers/servers_list/server_item/index.ts | 4 +- app/utils/helpers.ts | 37 +++++-------------- app/utils/push_proxy.ts | 10 ++--- 6 files changed, 51 insertions(+), 41 deletions(-) diff --git a/app/managers/session_manager.ts b/app/managers/session_manager.ts index c79a69cff..09b0c1301 100644 --- a/app/managers/session_manager.ts +++ b/app/managers/session_manager.ts @@ -21,7 +21,7 @@ import {getCurrentUser} from '@queries/servers/user'; import {getThemeFromState} from '@screens/navigation'; import EphemeralStore from '@store/ephemeral_store'; import {deleteFileCache, deleteFileCacheByDir} from '@utils/file'; -import {extractCleanDomain, isMainActivity} from '@utils/helpers'; +import {createKeyFromServerUrl, isMainActivity} from '@utils/helpers'; import {addNewServer} from '@utils/server'; import type {LaunchType} from '@typings/launch'; @@ -121,6 +121,7 @@ class SessionManager { WebsocketManager.invalidateClient(serverUrl); if (removeServer) { + await removePushDisabledInServerAcknowledged(createKeyFromServerUrl(serverUrl)); await DatabaseManager.destroyServerDatabase(serverUrl); } else { await DatabaseManager.deleteServerDatabase(serverUrl); @@ -168,8 +169,6 @@ class SessionManager { await this.terminateSession(serverUrl, removeServer); - await removePushDisabledInServerAcknowledged(extractCleanDomain(serverUrl)); - if (activeServerUrl === serverUrl) { let displayName = ''; let launchType: LaunchType = Launch.AddServer; 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 af40f111a..5959534ca 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'; @@ -23,4 +23,34 @@ describe('components/channel_list/header', () => { expect(toJSON()).toMatchSnapshot(); }); + + it('Push notifications disabled and not having ackoledge show alert icon', () => { + const wrapper = renderWithIntl( +
, + ); + + expect(wrapper.getByTestId('channel_list_header.push_alert')).toBeTruthy(); + }); + + it('Push notifications disabled but after ackoledging do not show alert icon', () => { + const wrapper = renderWithIntl( +
, + ); + + expect(wrapper.queryByTestId('channel_list_header.push_alert')).toBeNull(); + }); }); diff --git a/app/screens/home/channel_list/categories_list/header/index.ts b/app/screens/home/channel_list/categories_list/header/index.ts index 305883204..8d72dda1a 100644 --- a/app/screens/home/channel_list/categories_list/header/index.ts +++ b/app/screens/home/channel_list/categories_list/header/index.ts @@ -7,7 +7,7 @@ import {combineLatest, of as of$} from 'rxjs'; import {distinctUntilChanged, switchMap} from 'rxjs/operators'; import {observePushDisabledInServerAcknowledged} from '@app/queries/app/global'; -import {extractCleanDomain} from '@app/utils/helpers'; +import {createKeyFromServerUrl} from '@app/utils/helpers'; import {Permissions} from '@constants'; import {withServerUrl} from '@context/server'; import {observePermissionForTeam} from '@queries/servers/role'; @@ -64,7 +64,7 @@ const enhanced = withObservables([], ({serverUrl, database}: Props) => { distinctUntilChanged(), ), pushProxyStatus: observePushVerificationStatus(database), - pushDisabledAck: observePushDisabledInServerAcknowledged(extractCleanDomain(serverUrl)), + pushDisabledAck: observePushDisabledInServerAcknowledged(createKeyFromServerUrl(serverUrl)), }; }); diff --git a/app/screens/home/channel_list/servers/servers_list/server_item/index.ts b/app/screens/home/channel_list/servers/servers_list/server_item/index.ts index 626f5121d..dd0d83ff0 100644 --- a/app/screens/home/channel_list/servers/servers_list/server_item/index.ts +++ b/app/screens/home/channel_list/servers/servers_list/server_item/index.ts @@ -4,7 +4,7 @@ import withObservables from '@nozbe/with-observables'; import {of as of$} from 'rxjs'; -import {extractCleanDomain} from '@app/utils/helpers'; +import {createKeyFromServerUrl} from '@app/utils/helpers'; import {Tutorial} from '@constants'; import {PUSH_PROXY_STATUS_UNKNOWN} from '@constants/push_proxy'; import DatabaseManager from '@database/manager'; @@ -27,7 +27,7 @@ const enhance = withObservables(['highlight'], ({highlight, server}: {highlight: server: server.observe(), tutorialWatched, pushProxyStatus: serverDatabase ? observePushVerificationStatus(serverDatabase) : of$(PUSH_PROXY_STATUS_UNKNOWN), - pushDisabledAck: observePushDisabledInServerAcknowledged(extractCleanDomain(server.url)), + pushDisabledAck: observePushDisabledInServerAcknowledged(createKeyFromServerUrl(server.url)), }; }); diff --git a/app/utils/helpers.ts b/app/utils/helpers.ts index 715951d38..dabfafd30 100644 --- a/app/utils/helpers.ts +++ b/app/utils/helpers.ts @@ -164,46 +164,27 @@ export function isMainActivity() { } /** - * Extracts the domain name or IP address from a serverUrl. + * Uses the serverUrl to create a key. * * @param {string} input - The serverUrl, potentially with a port and protocol. * @returns {string} The extracted domain name or IP address, * or empty if no match is found. * * // Output: - * // Input: https://example-something.com => Result: example-something - * // Input: http://192.168.1.1 => Result: 192.168.1.1 - * // Input: https://127.0.0.1:3000 => Result: 127.0.0.1 - * // Input: https://subdomain.example.com/api => subdomain.example.com/api - * // Input: http://localhost:8080/app/v1 => localhost:8080/app/v1 + * // Input: https://example-something.com => Result: examplesomething + * // Input: http://192.168.1.1 => Result: 19216811 + * // Input: https://127.0.0.1:3000 => Result: 1270013000 + * // Input: https://subdomain.example.com/api => subdomainexamplecomapi + * // Input: http://localhost:8080/app/v1 => localhost8080appv1 */ -export function extractDomain(input: string) { +export function createKeyFromServerUrl(input: string) { const regex = /^(https?:\/\/)?([\w.-]+)(:\d+)?(\/\S*)?/; const match = input.match(regex); if (match) { - const modifiedString = match[2] + (match[4] || ''); - return modifiedString; + const domainWithPortAndSubpath = match[2] + (match[3] || '') + (match[4] || ''); + return domainWithPortAndSubpath.replace(/[^\w]/g, ''); } return ''; } - -/** - * Returns the domain name or IP address from a serverUrl without special characters. - * - * @param {string} domain - The extracted domain from a serverUrl. - * @returns {string} The extracted domain name or IP address, modified to remove special characters, - * or empty . - * - * // Output: - * // Input: example-something => Result: examplesomething - * // Input: 192.168.1.1 => Result: 19216811 - * // Input: 127.0.0.1 => Result: 127001 - */ -export function extractCleanDomain(domain: string) { - if (!domain) { - return ''; - } - return extractDomain(domain).replace(/[\W.]/g, ''); -} diff --git a/app/utils/push_proxy.ts b/app/utils/push_proxy.ts index 7bfba76c2..e44a35882 100644 --- a/app/utils/push_proxy.ts +++ b/app/utils/push_proxy.ts @@ -8,23 +8,23 @@ import {getPushDisabledInServerAcknowledged} from '@app/queries/app/global'; import {PUSH_PROXY_RESPONSE_NOT_AVAILABLE, PUSH_PROXY_RESPONSE_UNKNOWN, PUSH_PROXY_STATUS_NOT_AVAILABLE, PUSH_PROXY_STATUS_UNKNOWN, PUSH_PROXY_STATUS_VERIFIED} from '@constants/push_proxy'; import EphemeralStore from '@store/ephemeral_store'; -import {extractCleanDomain} from './helpers'; +import {createKeyFromServerUrl} from './helpers'; import type {IntlShape} from 'react-intl'; export async function pushDisabledInServerAck(serverUrl: string) { - const extractedDomain = extractCleanDomain(serverUrl); + const extractedDomain = createKeyFromServerUrl(serverUrl); const pushServerDisabledAck = await getPushDisabledInServerAcknowledged(extractedDomain); return pushServerDisabledAck; } export async function canReceiveNotifications(serverUrl: string, verification: string, intl: IntlShape) { - const a = await pushDisabledInServerAck(serverUrl); + const hasAckNotification = await pushDisabledInServerAck(serverUrl); switch (verification) { case PUSH_PROXY_RESPONSE_NOT_AVAILABLE: EphemeralStore.setPushProxyVerificationState(serverUrl, PUSH_PROXY_STATUS_NOT_AVAILABLE); - if (!a) { + if (!hasAckNotification) { alertPushProxyError(intl, serverUrl); } break; @@ -40,7 +40,7 @@ export async function canReceiveNotifications(serverUrl: string, verification: s const handleAlertResponse = async (buttonIndex: number, serverUrl: string) => { if (buttonIndex === 0) { // User clicked "Okay" acknowledging that the push notifications are disabled on that server - await storePushDisabledInServerAcknowledged(extractCleanDomain(serverUrl)); + await storePushDisabledInServerAcknowledged(createKeyFromServerUrl(serverUrl)); } }; From d4cd0cb1de50d13838647f0d4ef2d7d5179ac658 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Andr=C3=A9s=20V=C3=A9lez=20Vidal?= Date: Tue, 22 Aug 2023 15:50:45 +0200 Subject: [PATCH 4/7] implement pr feedback; reuse existing keyfromurl function; fix imports --- app/managers/session_manager.ts | 5 ++-- .../categories_list/header/header.test.tsx | 4 +-- .../categories_list/header/index.ts | 6 ++--- .../servers/servers_list/server_item/index.ts | 4 +-- app/utils/helpers.ts | 26 ------------------- app/utils/push_proxy.ts | 13 +++++----- 6 files changed, 16 insertions(+), 42 deletions(-) diff --git a/app/managers/session_manager.ts b/app/managers/session_manager.ts index 09b0c1301..f5bbe2562 100644 --- a/app/managers/session_manager.ts +++ b/app/managers/session_manager.ts @@ -7,6 +7,7 @@ import FastImage from 'react-native-fast-image'; import {removePushDisabledInServerAcknowledged, storeOnboardingViewedValue} from '@actions/app/global'; import {cancelSessionNotification, logout, scheduleSessionNotification} from '@actions/remote/session'; +import {urlSafeBase64Encode} from '@app/utils/security'; import {Events, Launch} from '@constants'; import DatabaseManager from '@database/manager'; import {resetMomentLocale} from '@i18n'; @@ -21,7 +22,7 @@ import {getCurrentUser} from '@queries/servers/user'; import {getThemeFromState} from '@screens/navigation'; import EphemeralStore from '@store/ephemeral_store'; import {deleteFileCache, deleteFileCacheByDir} from '@utils/file'; -import {createKeyFromServerUrl, isMainActivity} from '@utils/helpers'; +import {isMainActivity} from '@utils/helpers'; import {addNewServer} from '@utils/server'; import type {LaunchType} from '@typings/launch'; @@ -121,7 +122,7 @@ class SessionManager { WebsocketManager.invalidateClient(serverUrl); if (removeServer) { - await removePushDisabledInServerAcknowledged(createKeyFromServerUrl(serverUrl)); + await removePushDisabledInServerAcknowledged(urlSafeBase64Encode(serverUrl)); await DatabaseManager.destroyServerDatabase(serverUrl); } else { await DatabaseManager.deleteServerDatabase(serverUrl); 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 5959534ca..8414a7a71 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 @@ -24,7 +24,7 @@ describe('components/channel_list/header', () => { expect(toJSON()).toMatchSnapshot(); }); - it('Push notifications disabled and not having ackoledge show alert icon', () => { + it('Push notifications disabled and not having acknoledged it show alert icon', () => { const wrapper = renderWithIntl(
{ expect(wrapper.getByTestId('channel_list_header.push_alert')).toBeTruthy(); }); - it('Push notifications disabled but after ackoledging do not show alert icon', () => { + it('Push notifications are disabled, but even after acknowledging them, the alert icon does not appear', () => { const wrapper = renderWithIntl(
{ distinctUntilChanged(), ), pushProxyStatus: observePushVerificationStatus(database), - pushDisabledAck: observePushDisabledInServerAcknowledged(createKeyFromServerUrl(serverUrl)), + pushDisabledAck: observePushDisabledInServerAcknowledged(urlSafeBase64Encode(serverUrl)), }; }); diff --git a/app/screens/home/channel_list/servers/servers_list/server_item/index.ts b/app/screens/home/channel_list/servers/servers_list/server_item/index.ts index dd0d83ff0..be9090cc2 100644 --- a/app/screens/home/channel_list/servers/servers_list/server_item/index.ts +++ b/app/screens/home/channel_list/servers/servers_list/server_item/index.ts @@ -4,12 +4,12 @@ import withObservables from '@nozbe/with-observables'; import {of as of$} from 'rxjs'; -import {createKeyFromServerUrl} from '@app/utils/helpers'; import {Tutorial} from '@constants'; import {PUSH_PROXY_STATUS_UNKNOWN} from '@constants/push_proxy'; import DatabaseManager from '@database/manager'; import {observePushDisabledInServerAcknowledged, observeTutorialWatched} from '@queries/app/global'; import {observePushVerificationStatus} from '@queries/servers/system'; +import {urlSafeBase64Encode} from '@utils/security'; import ServerItem from './server_item'; @@ -27,7 +27,7 @@ const enhance = withObservables(['highlight'], ({highlight, server}: {highlight: server: server.observe(), tutorialWatched, pushProxyStatus: serverDatabase ? observePushVerificationStatus(serverDatabase) : of$(PUSH_PROXY_STATUS_UNKNOWN), - pushDisabledAck: observePushDisabledInServerAcknowledged(createKeyFromServerUrl(server.url)), + pushDisabledAck: observePushDisabledInServerAcknowledged(urlSafeBase64Encode(server.url)), }; }); diff --git a/app/utils/helpers.ts b/app/utils/helpers.ts index dabfafd30..cb1915c0c 100644 --- a/app/utils/helpers.ts +++ b/app/utils/helpers.ts @@ -162,29 +162,3 @@ export function isMainActivity() { android: ShareModule?.getCurrentActivityName() === 'MainActivity', }); } - -/** - * Uses the serverUrl to create a key. - * - * @param {string} input - The serverUrl, potentially with a port and protocol. - * @returns {string} The extracted domain name or IP address, - * or empty if no match is found. - * - * // Output: - * // Input: https://example-something.com => Result: examplesomething - * // Input: http://192.168.1.1 => Result: 19216811 - * // Input: https://127.0.0.1:3000 => Result: 1270013000 - * // Input: https://subdomain.example.com/api => subdomainexamplecomapi - * // Input: http://localhost:8080/app/v1 => localhost8080appv1 - */ -export function createKeyFromServerUrl(input: string) { - const regex = /^(https?:\/\/)?([\w.-]+)(:\d+)?(\/\S*)?/; - const match = input.match(regex); - - if (match) { - const domainWithPortAndSubpath = match[2] + (match[3] || '') + (match[4] || ''); - return domainWithPortAndSubpath.replace(/[^\w]/g, ''); - } - - return ''; -} diff --git a/app/utils/push_proxy.ts b/app/utils/push_proxy.ts index e44a35882..9ce7cb80a 100644 --- a/app/utils/push_proxy.ts +++ b/app/utils/push_proxy.ts @@ -4,18 +4,17 @@ import {Alert} from 'react-native'; import {storePushDisabledInServerAcknowledged} from '@actions/app/global'; -import {getPushDisabledInServerAcknowledged} from '@app/queries/app/global'; import {PUSH_PROXY_RESPONSE_NOT_AVAILABLE, PUSH_PROXY_RESPONSE_UNKNOWN, PUSH_PROXY_STATUS_NOT_AVAILABLE, PUSH_PROXY_STATUS_UNKNOWN, PUSH_PROXY_STATUS_VERIFIED} from '@constants/push_proxy'; +import {getPushDisabledInServerAcknowledged} from '@queries/app/global'; import EphemeralStore from '@store/ephemeral_store'; -import {createKeyFromServerUrl} from './helpers'; +import {urlSafeBase64Encode} from './security'; import type {IntlShape} from 'react-intl'; -export async function pushDisabledInServerAck(serverUrl: string) { - const extractedDomain = createKeyFromServerUrl(serverUrl); - const pushServerDisabledAck = await getPushDisabledInServerAcknowledged(extractedDomain); - return pushServerDisabledAck; +export function pushDisabledInServerAck(serverUrl: string) { + const extractedDomain = urlSafeBase64Encode(serverUrl); + return getPushDisabledInServerAcknowledged(extractedDomain); } export async function canReceiveNotifications(serverUrl: string, verification: string, intl: IntlShape) { @@ -40,7 +39,7 @@ export async function canReceiveNotifications(serverUrl: string, verification: s const handleAlertResponse = async (buttonIndex: number, serverUrl: string) => { if (buttonIndex === 0) { // User clicked "Okay" acknowledging that the push notifications are disabled on that server - await storePushDisabledInServerAcknowledged(createKeyFromServerUrl(serverUrl)); + await storePushDisabledInServerAcknowledged(urlSafeBase64Encode(serverUrl)); } }; From 41ca61966bcf035fb692d5988e401f154fb9b5d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Andr=C3=A9s=20V=C3=A9lez=20Vidal?= Date: Tue, 10 Oct 2023 17:10:44 +0200 Subject: [PATCH 5/7] do not remove inside app alerts --- app/managers/session_manager.ts | 2 +- .../categories_list/header/header.test.tsx | 19 +------------------ .../categories_list/header/header.tsx | 6 ++---- .../categories_list/header/index.ts | 12 ++---------- .../servers/servers_list/server_item/index.ts | 4 +--- .../servers_list/server_item/server_item.tsx | 4 +--- app/utils/push_proxy.ts | 6 +++--- 7 files changed, 11 insertions(+), 42 deletions(-) diff --git a/app/managers/session_manager.ts b/app/managers/session_manager.ts index f5bbe2562..1ba1fbbb0 100644 --- a/app/managers/session_manager.ts +++ b/app/managers/session_manager.ts @@ -7,7 +7,6 @@ import FastImage from 'react-native-fast-image'; import {removePushDisabledInServerAcknowledged, storeOnboardingViewedValue} from '@actions/app/global'; import {cancelSessionNotification, logout, scheduleSessionNotification} from '@actions/remote/session'; -import {urlSafeBase64Encode} from '@app/utils/security'; import {Events, Launch} from '@constants'; import DatabaseManager from '@database/manager'; import {resetMomentLocale} from '@i18n'; @@ -23,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'; 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 8414a7a71..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 @@ -17,14 +17,13 @@ describe('components/channel_list/header', () => { canJoinChannels={true} canInvitePeople={true} displayName={'Test!'} - pushDisabledAck={true} />, ); expect(toJSON()).toMatchSnapshot(); }); - it('Push notifications disabled and not having acknoledged it show alert icon', () => { + it('Push notifications disabled show alert icon', () => { const wrapper = renderWithIntl(
{ canJoinChannels={true} canInvitePeople={true} displayName={'Test!'} - pushDisabledAck={false} />, ); expect(wrapper.getByTestId('channel_list_header.push_alert')).toBeTruthy(); }); - - it('Push notifications are disabled, but even after acknowledging them, the alert icon does not appear', () => { - const wrapper = renderWithIntl( -
, - ); - - expect(wrapper.queryByTestId('channel_list_header.push_alert')).toBeNull(); - }); }); 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 fb1528d9b..ca2840642 100644 --- a/app/screens/home/channel_list/categories_list/header/header.tsx +++ b/app/screens/home/channel_list/categories_list/header/header.tsx @@ -38,7 +38,6 @@ type Props = { iconPad?: boolean; onHeaderPress?: () => void; pushProxyStatus: string; - pushDisabledAck: boolean; } const getStyles = makeStyleSheetFromTheme((theme: Theme) => ({ @@ -112,7 +111,6 @@ const ChannelListHeader = ({ iconPad, onHeaderPress, pushProxyStatus, - pushDisabledAck, }: Props) => { const theme = useTheme(); const isTablet = useIsTablet(); @@ -168,7 +166,7 @@ const ChannelListHeader = ({ const onPushAlertPress = useCallback(() => { if (pushProxyStatus === PUSH_PROXY_STATUS_NOT_AVAILABLE) { - alertPushProxyError(intl, serverUrl); + alertPushProxyError(intl); } else { alertPushProxyUnknown(intl); } @@ -208,7 +206,7 @@ const ChannelListHeader = ({ > {serverDisplayName} - {pushProxyStatus !== PUSH_PROXY_STATUS_VERIFIED && !pushDisabledAck && ( + {pushProxyStatus !== PUSH_PROXY_STATUS_VERIFIED && ( { +const enhanced = withObservables([], ({database}: WithDatabaseArgs) => { const team = observeCurrentTeam(database); const currentUser = observeCurrentUser(database); @@ -64,8 +57,7 @@ const enhanced = withObservables([], ({serverUrl, database}: Props) => { distinctUntilChanged(), ), pushProxyStatus: observePushVerificationStatus(database), - pushDisabledAck: observePushDisabledInServerAcknowledged(urlSafeBase64Encode(serverUrl)), }; }); -export default withDatabase(withServerUrl(enhanced(ChannelListHeader))); +export default withDatabase(enhanced(ChannelListHeader)); diff --git a/app/screens/home/channel_list/servers/servers_list/server_item/index.ts b/app/screens/home/channel_list/servers/servers_list/server_item/index.ts index be9090cc2..a46828e71 100644 --- a/app/screens/home/channel_list/servers/servers_list/server_item/index.ts +++ b/app/screens/home/channel_list/servers/servers_list/server_item/index.ts @@ -7,9 +7,8 @@ import {of as of$} from 'rxjs'; import {Tutorial} from '@constants'; import {PUSH_PROXY_STATUS_UNKNOWN} from '@constants/push_proxy'; import DatabaseManager from '@database/manager'; -import {observePushDisabledInServerAcknowledged, observeTutorialWatched} from '@queries/app/global'; +import {observeTutorialWatched} from '@queries/app/global'; import {observePushVerificationStatus} from '@queries/servers/system'; -import {urlSafeBase64Encode} from '@utils/security'; import ServerItem from './server_item'; @@ -27,7 +26,6 @@ const enhance = withObservables(['highlight'], ({highlight, server}: {highlight: server: server.observe(), tutorialWatched, pushProxyStatus: serverDatabase ? observePushVerificationStatus(serverDatabase) : of$(PUSH_PROXY_STATUS_UNKNOWN), - pushDisabledAck: observePushDisabledInServerAcknowledged(urlSafeBase64Encode(server.url)), }; }); diff --git a/app/screens/home/channel_list/servers/servers_list/server_item/server_item.tsx b/app/screens/home/channel_list/servers/servers_list/server_item/server_item.tsx index 0f463cba6..efe10083e 100644 --- a/app/screens/home/channel_list/servers/servers_list/server_item/server_item.tsx +++ b/app/screens/home/channel_list/servers/servers_list/server_item/server_item.tsx @@ -44,7 +44,6 @@ type Props = { server: ServersModel; tutorialWatched: boolean; pushProxyStatus: string; - pushDisabledAck: boolean; } type BadgeValues = { @@ -148,7 +147,6 @@ const ServerItem = ({ server, tutorialWatched, pushProxyStatus, - pushDisabledAck, }: Props) => { const intl = useIntl(); const theme = useTheme(); @@ -430,7 +428,7 @@ const ServerItem = ({ > {displayName} - {server.lastActiveAt > 0 && pushProxyStatus !== PUSH_PROXY_STATUS_VERIFIED && !pushDisabledAck && ( + {server.lastActiveAt > 0 && pushProxyStatus !== PUSH_PROXY_STATUS_VERIFIED && ( { - if (buttonIndex === 0) { +const handleAlertResponse = async (buttonIndex: number, serverUrl?: string) => { + 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) { +export function alertPushProxyError(intl: IntlShape, serverUrl?: string) { Alert.alert( intl.formatMessage({ id: 'alert.push_proxy_error.title', From 89fc2e8b997d7602137d98059c07295455bddbc3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Andr=C3=A9s=20V=C3=A9lez=20Vidal?= Date: Wed, 18 Oct 2023 12:12:37 +0200 Subject: [PATCH 6/7] Update alert text --- app/utils/push_proxy.ts | 2 +- assets/base/i18n/en.json | 2 +- assets/base/i18n/en_AU.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/utils/push_proxy.ts b/app/utils/push_proxy.ts index 4dcb3cd3b..332d720e2 100644 --- a/app/utils/push_proxy.ts +++ b/app/utils/push_proxy.ts @@ -51,7 +51,7 @@ export function alertPushProxyError(intl: IntlShape, serverUrl?: string) { }), 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'}), diff --git a/assets/base/i18n/en.json b/assets/base/i18n/en.json index eb879a5e3..bd201771e 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 0a8a601d3..3dc5875e4 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", From 6dcc999176e75069dca6332de3a0d8a16a874c52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Andr=C3=A9s=20V=C3=A9lez=20Vidal?= Date: Wed, 18 Oct 2023 21:06:12 +0200 Subject: [PATCH 7/7] capitalize back the alert text --- app/utils/push_proxy.ts | 2 +- assets/base/i18n/en.json | 2 +- assets/base/i18n/en_AU.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/utils/push_proxy.ts b/app/utils/push_proxy.ts index 332d720e2..ec84c91c3 100644 --- a/app/utils/push_proxy.ts +++ b/app/utils/push_proxy.ts @@ -51,7 +51,7 @@ export function alertPushProxyError(intl: IntlShape, serverUrl?: string) { }), intl.formatMessage({ id: 'alert.push_proxy_error.description', - defaultMessage: 'due to the configuration of 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'}), diff --git a/assets/base/i18n/en.json b/assets/base/i18n/en.json index bd201771e..9ef4dd764 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 of 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 3dc5875e4..aa9654932 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 of 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",