feat: test push notification (#5329)

* feat: test push notification

* restApi and definition

* push.info and change properly the troubleshootingNotification

* use the finally at try/catch

* minor tweak
This commit is contained in:
Reinaldo Neto 2023-11-10 11:24:14 -03:00 committed by GitHub
parent 5ab5d9c945
commit 85ec50a9ee
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 119 additions and 109 deletions

View File

@ -96,8 +96,4 @@ export const VIDEO_CONF = createRequestTypes('VIDEO_CONF', [
'ACCEPT_CALL',
'SET_CALLING'
]);
export const TROUBLESHOOTING_NOTIFICATION = createRequestTypes('TROUBLESHOOTING_NOTIFICATION', [
'REQUEST',
'SET',
'SET_IN_ALERT'
]);
export const TROUBLESHOOTING_NOTIFICATION = createRequestTypes('TROUBLESHOOTING_NOTIFICATION', ['REQUEST', 'SET']);

View File

@ -23,12 +23,3 @@ export function setTroubleshootingNotification(payload: Partial<ITroubleshooting
payload
};
}
export function setInAlertTroubleshootingNotification(
payload: Pick<ITroubleshootingNotification, 'inAlertNotification'>
): TSetInAlertTroubleshootingNotification {
return {
type: TROUBLESHOOTING_NOTIFICATION.SET_IN_ALERT,
payload
};
}

View File

@ -17,7 +17,7 @@ import { E2eEndpoints } from './e2e';
import { SubscriptionsEndpoints } from './subscriptions';
import { VideoConferenceEndpoints } from './videoConference';
import { CommandsEndpoints } from './commands';
import { PushTokenEndpoints } from './pushToken';
import { PushEndpoints } from './push';
import { DirectoryEndpoint } from './directory';
import { AutoTranslateEndpoints } from './autotranslate';
@ -40,6 +40,6 @@ export type Endpoints = ChannelsEndpoints &
SubscriptionsEndpoints &
VideoConferenceEndpoints &
CommandsEndpoints &
PushTokenEndpoints &
PushEndpoints &
DirectoryEndpoint &
AutoTranslateEndpoints;

View File

@ -1,4 +1,10 @@
export type PushTokenEndpoints = {
type TPushInfo = {
pushGatewayEnabled: boolean;
defaultPushGateway: boolean;
success: boolean;
};
export type PushEndpoints = {
'push.token': {
POST: (params: { value: string; type: string; appName: string }) => {
result: {
@ -9,4 +15,7 @@ export type PushTokenEndpoints = {
};
};
};
'push.info': {
GET: () => TPushInfo;
};
};

View File

@ -770,5 +770,6 @@
"Jitsi_authentication_before_making_calls": "Jitsi may require authentication before making calls. To learn more about their policies, visit the Jitsi website.",
"Jitsi_authentication_before_making_calls_ask_admin": "If you believe there are problems with Jitsi and its authentication, ask a workspace administrator for help.",
"Continue": "Continue",
"Troubleshooting": "Troubleshooting"
"Troubleshooting": "Troubleshooting",
"Your_push_was_sent_to_s_devices": "Your push was sent to {{s}} devices"
}

View File

@ -756,5 +756,6 @@
"decline": "Recusar",
"accept": "Aceitar",
"Incoming_call_from": "Chamada recebida de",
"Call_started": "Chamada Iniciada"
"Call_started": "Chamada Iniciada",
"Your_push_was_sent_to_s_devices": "A sua notificação foi enviada para {{s}} dispositivos"
}

View File

@ -60,7 +60,8 @@ export const SUPPORTED_PERMISSIONS = [
'view-canned-responses',
'mobile-upload-file',
'delete-own-message',
'call-management'
'call-management',
'test-push-notifications'
] as const;
export async function setPermissions(): Promise<void> {

View File

@ -901,6 +901,11 @@ export const removePushToken = (): Promise<boolean | void> => {
return Promise.resolve();
};
export const pushTest = (): Promise<{ message: string; params: number[] }> => sdk.methodCallWrapper('push_test');
// RC 6.5.0
export const pushInfo = () => sdk.get('push.info');
export const sendEmailCode = () => {
const { username } = reduxStore.getState().login.user as IUser;
// RC 3.1.0

View File

@ -1,4 +1,4 @@
import { setInAlertTroubleshootingNotification, setTroubleshootingNotification } from '../actions/troubleshootingNotification';
import { setTroubleshootingNotification, requestTroubleshootingNotification } from '../actions/troubleshootingNotification';
import { mockedStore } from './mockedStore';
import { ITroubleshootingNotification, initialState } from './troubleshootingNotification';
@ -8,26 +8,21 @@ describe('test troubleshootingNotification reducer', () => {
expect(state).toEqual(initialState);
});
it('should return correctly the value after call requestTroubleshootingNotification action', () => {
mockedStore.dispatch(requestTroubleshootingNotification());
const state = mockedStore.getState().troubleshootingNotification;
expect(state).toEqual(initialState);
});
it('should return correctly value after call troubleshootingNotification action', () => {
const payload: ITroubleshootingNotification = {
consumptionPercentage: 50,
deviceNotificationEnabled: true,
inAlertNotification: false,
isCommunityEdition: true,
isCustomPushGateway: true,
isPushGatewayConnected: true
defaultPushGateway: true,
pushGatewayEnabled: true
};
mockedStore.dispatch(setTroubleshootingNotification(payload));
const state = mockedStore.getState().troubleshootingNotification;
expect(state).toEqual(payload);
});
it('should return correctly the inAlert value after call setInAlert action', () => {
const previousInAlertState = mockedStore.getState().troubleshootingNotification.inAlertNotification;
const payload: Pick<ITroubleshootingNotification, 'inAlertNotification'> = {
inAlertNotification: !previousInAlertState
};
mockedStore.dispatch(setInAlertTroubleshootingNotification(payload));
const newInAlertState = mockedStore.getState().troubleshootingNotification.inAlertNotification;
expect(newInAlertState).toEqual(payload.inAlertNotification);
});
});

View File

@ -3,20 +3,19 @@ import { TActionTroubleshootingNotification } from '../actions/troubleshootingNo
export interface ITroubleshootingNotification {
deviceNotificationEnabled: boolean;
isCommunityEdition: boolean;
isPushGatewayConnected: boolean;
isCustomPushGateway: boolean;
consumptionPercentage: number;
pushGatewayEnabled: boolean;
defaultPushGateway: boolean;
inAlertNotification: boolean;
}
export const initialState: ITroubleshootingNotification = {
consumptionPercentage: 0,
deviceNotificationEnabled: false,
isCommunityEdition: false,
isPushGatewayConnected: false,
isCustomPushGateway: false,
pushGatewayEnabled: false,
defaultPushGateway: false,
inAlertNotification: false
// TODO: This will be used in the near future when the consumption percentage is implemented on the server.
// consumptionPercentage: 0,
// isCommunityEdition: false,
};
export default (state = initialState, action: TActionTroubleshootingNotification): ITroubleshootingNotification => {
@ -26,11 +25,6 @@ export default (state = initialState, action: TActionTroubleshootingNotification
...state,
...action.payload
};
case TROUBLESHOOTING_NOTIFICATION.SET_IN_ALERT:
return {
...state,
...action.payload
};
default:
return state;
}

View File

@ -15,6 +15,7 @@ import { inviteLinksRequest } from '../actions/inviteLinks';
import { showErrorAlert } from '../lib/methods/helpers/info';
import { localAuthenticate } from '../lib/methods/helpers/localAuthentication';
import { encryptionInit, encryptionStop } from '../actions/encryption';
import { requestTroubleshootingNotification } from '../actions/troubleshootingNotification';
import UserPreferences from '../lib/methods/userPreferences';
import { inquiryRequest, inquiryReset } from '../ee/omnichannel/actions/inquiry';
import { isOmnichannelStatusAvailable } from '../ee/omnichannel/lib';
@ -209,6 +210,7 @@ const handleLoginSuccess = function* handleLoginSuccess({ user }) {
if (inviteLinkToken) {
yield put(inviteLinksRequest(inviteLinkToken));
}
yield put(requestTroubleshootingNotification());
} catch (e) {
log(e);
}

View File

@ -3,34 +3,39 @@ import { put, takeEvery } from 'redux-saga/effects';
import { call } from 'typed-redux-saga';
import notifee from '@notifee/react-native';
import { ITroubleshootingNotification } from '../reducers/troubleshootingNotification';
import { TROUBLESHOOTING_NOTIFICATION } from '../actions/actionsTypes';
import { setInAlertTroubleshootingNotification, setTroubleshootingNotification } from '../actions/troubleshootingNotification';
import { appSelector } from '../lib/hooks';
import { setTroubleshootingNotification } from '../actions/troubleshootingNotification';
import { pushInfo } from '../lib/services/restApi';
import log from '../lib/methods/helpers/log';
interface IGenericAction extends Action {
type: string;
}
type TSetGeneric = IGenericAction & {
payload: ITroubleshootingNotification;
};
function* request() {
const settings = yield* call(notifee.getNotificationSettings);
yield put(setTroubleshootingNotification({ deviceNotificationEnabled: !!settings.authorizationStatus }));
}
function* setNotification({ payload }: { payload: ITroubleshootingNotification }) {
const troubleshootingNotification = yield* appSelector(state => state.troubleshootingNotification);
const newState = { ...troubleshootingNotification, ...payload };
// TODO: add properly the conditions to set inAlertNotification bias on each expected settings
// For now there is only the deviceNotificationEnabled properly, waiting for the next settings to fix
const inAlertNotification = !newState.deviceNotificationEnabled;
yield put(setInAlertTroubleshootingNotification({ inAlertNotification }));
let deviceNotificationEnabled = false;
let defaultPushGateway = false;
let pushGatewayEnabled = false;
try {
const { authorizationStatus } = yield* call(notifee.getNotificationSettings);
deviceNotificationEnabled = authorizationStatus > 0;
const pushInfoResult = yield* call(pushInfo);
if (pushInfoResult.success) {
pushGatewayEnabled = pushInfoResult.pushGatewayEnabled;
defaultPushGateway = pushInfoResult.defaultPushGateway;
}
} catch (e) {
log(e);
} finally {
// If Any of the items that can have red values: notification settings, CE quota, or gateway connection; the red icon should show.
// Then inAlertNotification has to be true
const inAlertNotification = !deviceNotificationEnabled || !pushGatewayEnabled;
yield put(
setTroubleshootingNotification({ deviceNotificationEnabled, defaultPushGateway, pushGatewayEnabled, inAlertNotification })
);
}
}
export default function* root(): Generator {
yield takeEvery<IGenericAction>(TROUBLESHOOTING_NOTIFICATION.REQUEST, request);
yield takeEvery<TSetGeneric>(TROUBLESHOOTING_NOTIFICATION.SET, setNotification);
}

View File

@ -11,10 +11,12 @@ import I18n from '../../i18n';
import { SettingsStackParamList } from '../../stacks/types';
import { useTheme } from '../../theme';
import CustomListSection from './components/CustomListSection';
import ListPercentage from './components/ListPercentage';
import { isIOS, showErrorAlert } from '../../lib/methods/helpers';
import { compareServerVersion, isIOS, showErrorAlert } from '../../lib/methods/helpers';
import { requestTroubleshootingNotification } from '../../actions/troubleshootingNotification';
import { useAppSelector } from '../../lib/hooks';
import { useAppSelector, usePermissions } from '../../lib/hooks';
import { Services } from '../../lib/services';
// TODO: This will be used in the near future when the consumption percentage is implemented on the server.
// import ListPercentage from './components/ListPercentage';
interface IPushTroubleshootViewProps {
navigation: StackNavigationProp<SettingsStackParamList, 'PushTroubleshootView'>;
@ -24,21 +26,20 @@ const PushTroubleshootView = ({ navigation }: IPushTroubleshootViewProps): JSX.E
const { colors } = useTheme();
const dispatch = useDispatch();
const {
consumptionPercentage,
deviceNotificationEnabled,
isCommunityEdition,
isCustomPushGateway,
isPushGatewayConnected,
foreground
} = useAppSelector(state => ({
deviceNotificationEnabled: state.troubleshootingNotification.deviceNotificationEnabled,
isCommunityEdition: state.troubleshootingNotification.isCommunityEdition,
isPushGatewayConnected: state.troubleshootingNotification.isPushGatewayConnected,
isCustomPushGateway: state.troubleshootingNotification.isCustomPushGateway,
consumptionPercentage: state.troubleshootingNotification.consumptionPercentage,
foreground: state.app.foreground
}));
const { deviceNotificationEnabled, defaultPushGateway, pushGatewayEnabled, foreground, serverVersion } = useAppSelector(
state => ({
deviceNotificationEnabled: state.troubleshootingNotification.deviceNotificationEnabled,
pushGatewayEnabled: state.troubleshootingNotification.pushGatewayEnabled,
defaultPushGateway: state.troubleshootingNotification.defaultPushGateway,
foreground: state.app.foreground,
serverVersion: state.server.version
// TODO: This will be used in the near future when the consumption percentage is implemented on the server.
// isCommunityEdition: state.troubleshootingNotification.isCommunityEdition,
// consumptionPercentage: state.troubleshootingNotification.consumptionPercentage,
})
);
const [testPushNotificationsPermission] = usePermissions(['test-push-notifications']);
useEffect(() => {
if (foreground) {
@ -64,9 +65,10 @@ const PushTroubleshootView = ({ navigation }: IPushTroubleshootViewProps): JSX.E
);
};
const alertWorkspaceConsumption = () => {
Alert.alert(I18n.t('Push_consumption_alert_title'), I18n.t('Push_consumption_alert_description'));
};
// TODO: This will be used in the near future when the consumption percentage is implemented on the server.
// const alertWorkspaceConsumption = () => {
// Alert.alert(I18n.t('Push_consumption_alert_title'), I18n.t('Push_consumption_alert_description'));
// };
const goToNotificationSettings = () => {
if (isIOS) {
@ -76,17 +78,25 @@ const PushTroubleshootView = ({ navigation }: IPushTroubleshootViewProps): JSX.E
}
};
const handleTestPushNotification = () => {
// do nothing
const handleTestPushNotification = async () => {
let message = '';
try {
const result = await Services.pushTest();
message = I18n.t('Your_push_was_sent_to_s_devices', { s: result.params[0] });
} catch (error: any) {
message = I18n.isTranslated(error?.error) ? I18n.t(error?.error) : error?.message;
} finally {
Alert.alert(I18n.t('Test_push_notification'), message);
}
};
let pushGatewayInfoDescription = 'Push_gateway_not_connected_description';
let pushGatewayStatusColor = colors.userPresenceBusy;
if (isPushGatewayConnected) {
if (pushGatewayEnabled) {
pushGatewayStatusColor = colors.userPresenceOnline;
pushGatewayInfoDescription = 'Push_gateway_connected_description';
}
if (isPushGatewayConnected && isCustomPushGateway) {
if (pushGatewayEnabled && !defaultPushGateway) {
pushGatewayStatusColor = colors.badgeBackgroundLevel3;
pushGatewayInfoDescription = 'Custom_push_gateway_connected_description';
}
@ -108,6 +118,7 @@ const PushTroubleshootView = ({ navigation }: IPushTroubleshootViewProps): JSX.E
<List.Separator />
</CustomListSection>
{/* TODO: This will be used in the near future when the consumption percentage is implemented on the server.
{isCommunityEdition ? (
<List.Section title='Community_edition_push_quota'>
<List.Separator />
@ -120,22 +131,24 @@ const PushTroubleshootView = ({ navigation }: IPushTroubleshootViewProps): JSX.E
<List.Separator />
<List.Info info='Workspace_consumption_description' />
</List.Section>
) : null}
) : null} */}
<CustomListSection
title={isCustomPushGateway ? 'Custom_push_gateway_connection' : 'Push_gateway_connection'}
statusColor={pushGatewayStatusColor}
>
<List.Separator />
<List.Item
title='Test_push_notification'
disabled={!isPushGatewayConnected}
onPress={handleTestPushNotification}
testID='push-troubleshoot-view-push-gateway-connection'
/>
<List.Separator />
<List.Info info={pushGatewayInfoDescription} />
</CustomListSection>
{compareServerVersion(serverVersion, 'greaterThanOrEqualTo', '6.5.0') ? (
<CustomListSection
title={!defaultPushGateway ? 'Custom_push_gateway_connection' : 'Push_gateway_connection'}
statusColor={pushGatewayStatusColor}
>
<List.Separator />
<List.Item
title='Test_push_notification'
disabled={!pushGatewayEnabled || !testPushNotificationsPermission}
onPress={handleTestPushNotification}
testID='push-troubleshoot-view-push-gateway-connection'
/>
<List.Separator />
<List.Info info={pushGatewayInfoDescription} />
</CustomListSection>
) : null}
<List.Section title='Notification_delay'>
<List.Separator />

View File

@ -15,7 +15,6 @@ import RoomItem, { ROW_HEIGHT, ROW_HEIGHT_CONDENSED } from '../../containers/Roo
import log, { logEvent, events } from '../../lib/methods/helpers/log';
import I18n from '../../i18n';
import { closeSearchHeader, closeServerDropdown, openSearchHeader, roomsRequest } from '../../actions/rooms';
import { requestTroubleshootingNotification } from '../../actions/troubleshootingNotification';
import * as HeaderButton from '../../containers/HeaderButton';
import StatusBar from '../../containers/StatusBar';
import ActivityIndicator from '../../containers/ActivityIndicator';
@ -200,8 +199,6 @@ class RoomsListView extends React.Component<IRoomsListViewProps, IRoomsListViewS
const { navigation, dispatch } = this.props;
this.handleHasPermission();
this.mounted = true;
dispatch(requestTroubleshootingNotification());
this.unsubscribeFocus = navigation.addListener('focus', () => {
this.animated = true;
// Check if there were changes with sort preference, then call getSubscription to remount the list