Skip to content

Commit

Permalink
fix: Allow "Take Now" to start when respondent is not explicitly assi…
Browse files Browse the repository at this point in the history
…gned (M2-7796) (#536)

* Update `useStartSurvey` hook to make `applet` optional.

- This allows unconditional usage of the hook in higher-level components where `applet` is not guaranteed.
- If the properties used by the applet are not available (ie, the applet is not provided), then the hook will simply not navigate when `startSurvey` is called.

* Remove existing "Take Now redirect" behaviour in ActivityCard.

- These cards may not be rendered if not explicitly assigned. This prevented "Take Now" from reliably starting.

* Add `useTakeNowRedirect` hook.

- This hook accepts a number of parameters required to initialize a "Take Now" activity/flow.
- It will return without taking action when called with insufficiently defined arguments.
- It will call `startSurvey`, thereby redirecting the use to the appropriate activity/flow when called with appropriate arguments.

* Add `useTakeNowRedirect` hook to `ActivityGroups` component.

* Address QA findings.

- Avoid displaying multi-informant/target subject banner UI.
  • Loading branch information
JustinNusca authored Oct 16, 2024
1 parent cfac41b commit 16dedf3
Show file tree
Hide file tree
Showing 4 changed files with 107 additions and 32 deletions.
18 changes: 10 additions & 8 deletions src/features/PassSurvey/hooks/useStartSurvey.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ActivityStatus, EntityType } from '~/abstract/lib/GroupBuilder';
import { EntityType } from '~/abstract/lib/GroupBuilder';
import { appletModel } from '~/entities/applet';
import { AppletBaseDTO } from '~/shared/api';
import ROUTES from '~/shared/constants/routes';
Expand All @@ -23,21 +23,19 @@ type OnActivityCardClickProps = {
eventId: string;
targetSubjectId: string | null;
flowId: string | null;
status: ActivityStatus;
shouldRestart: boolean;
};

type Props = {
applet: AppletBaseDTO;
applet?: AppletBaseDTO;
isPublic: boolean;
publicAppletKey: string | null;
};

export const useStartSurvey = (props: Props) => {
const navigator = useCustomNavigation();

const appletId = props.applet.id;
const flows = props.applet.activityFlows;
const appletId = props.applet?.id;
const flows = props.applet?.activityFlows;

const { removeGroupProgress } = appletModel.hooks.useGroupProgressStateManager();

Expand All @@ -47,6 +45,10 @@ export const useStartSurvey = (props: Props) => {
appletModel.hooks.useMultiInformantState();

function navigateToEntity(params: NavigateToEntityProps) {
if (!appletId) {
return;
}

const { activityId, flowId, eventId, targetSubjectId, entityType } = params;

if (props.isPublic && props.publicAppletKey) {
Expand Down Expand Up @@ -82,7 +84,7 @@ export const useStartSurvey = (props: Props) => {
shouldRestart,
}: OnActivityCardClickProps) {
const analyticsPayload: MixpanelPayload = {
[MixpanelProps.AppletId]: props.applet.id,
[MixpanelProps.AppletId]: appletId,
[MixpanelProps.ActivityId]: activityId,
};

Expand All @@ -102,7 +104,7 @@ export const useStartSurvey = (props: Props) => {
Mixpanel.track(MixpanelEvents.AssessmentStarted, analyticsPayload);

if (flowId) {
const flow = flows.find((x) => x.id === flowId);
const flow = flows?.find((x) => x.id === flowId);
const firstActivityId: string | null = flow?.activityIds[0] ?? null;

if (!firstActivityId) {
Expand Down
80 changes: 80 additions & 0 deletions src/widgets/ActivityGroups/model/hooks/useTakeNowRedirect.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import { useMemo } from 'react';

import { openStoreLink } from '~/abstract/lib';
import { isSupportedActivity } from '~/entities/activity';
import { useStartSurvey } from '~/features/PassSurvey';
import { AppletBaseDTO, AppletEventsResponse, HydratedAssignmentDTO } from '~/shared/api';

export function useTakeNowRedirect({
activityOrFlowId,
applet,
assignments = [],
events = [],
isPublic = false,
publicAppletKey = null,
}: {
activityOrFlowId?: string | null;
applet?: AppletBaseDTO;
assignments?: HydratedAssignmentDTO[] | null;
events?: AppletEventsResponse['events'];
isPublic?: boolean;
publicAppletKey?: string | null;
}) {
const { startSurvey } = useStartSurvey({ applet, isPublic, publicAppletKey });

const handleTakeNowRedirect = useMemo(
() =>
({
activityId,
eventId,
flowId = null,
isSupported = true,
}: {
activityId: string;
eventId: string;
flowId?: string | null;
isSupported?: boolean;
}) => {
if (!isSupported) {
openStoreLink();
return;
}

startSurvey({ activityId, eventId, flowId, shouldRestart: true, targetSubjectId: null });
},
[startSurvey],
);

if (assignments && applet && events && activityOrFlowId) {
const activity = applet.activities.find(({ id }) => id === activityOrFlowId);
const flow = applet.activityFlows.find(({ id }) => id === activityOrFlowId);
const event = events.find(({ entityId }) => entityId === (activity?.id || flow?.id));

if (!event) {
return;
}

if (activity) {
return handleTakeNowRedirect({
activityId: activity.id,
isSupported: isSupportedActivity(activity.containsResponseTypes),
eventId: event.id,
});
}

if (flow) {
const flowActivities = flow.activityIds.map((activityId) =>
applet.activities.find(({ id }) => activityId === id),
);

return handleTakeNowRedirect({
activityId: flow.activityIds[0],
isSupported: flowActivities.every(
(activity) => !!activity && isSupportedActivity(activity.containsResponseTypes),
),
eventId: event.id,
flowId: flow.id,
});
}
}
}
20 changes: 0 additions & 20 deletions src/widgets/ActivityGroups/ui/ActivityCard/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ import {
useAppSelector,
useCustomMediaQuery,
MixpanelPayload,
useOnceLayoutEffect,
} from '~/shared/utils';
import { TargetSubjectLabel } from '~/widgets/TargetSubjectLabel';

Expand Down Expand Up @@ -107,7 +106,6 @@ export const ActivityCard = ({ activityListItem }: Props) => {
activityId: activityListItem.activityId,
eventId: activityListItem.eventId,
targetSubjectId: activityListItem.targetSubject?.id ?? null,
status: activityListItem.status,
flowId: activityListItem.flowId,
shouldRestart,
});
Expand Down Expand Up @@ -143,24 +141,6 @@ export const ActivityCard = ({ activityListItem }: Props) => {
Mixpanel.track(MixpanelEvents.ActivityResumed, analyticsPayload);
};

// Start activity on mount if doing Take Now on this activity.
useOnceLayoutEffect(() => {
if (
context.startActivityOrFlow &&
// TODO: This `!activityListItem.targetSubject` check is added to ensure that Take Now is
// only triggered once, on the singular self-report instance of ActivityCard. However, this
// causes Take Now to fail when there is no self-report card, which is a possible scenario
// with the introduction of MI assignments. Remove this check when Take Now is refactored in
// https://mindlogger.atlassian.net/browse/M2-7796
!activityListItem.targetSubject &&
((!isFlow && context.startActivityOrFlow === activityListItem.activityId) ||
(isFlow && context.startActivityOrFlow === activityListItem.flowId))
) {
// Pass `true` to ensure activity doesn't resume from a previous progress state.
onStartActivity(true);
}
});

return (
<ActivityCardBase isDisabled={isDisabled} isFlow={isFlow}>
<Box
Expand Down
21 changes: 17 additions & 4 deletions src/widgets/ActivityGroups/ui/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { useLocation, useNavigate } from 'react-router-dom';

import { ActivityGroupList } from './ActivityGroupList';
import { AppletDetailsContext } from '../lib';
import { useTakeNowRedirect } from '../model/hooks/useTakeNowRedirect';

import { appletModel, useAppletBaseInfoByIdQuery } from '~/entities/applet';
import { useMyAssignmentsQuery } from '~/entities/assignment';
Expand All @@ -20,17 +21,20 @@ type PublicAppletDetails = {
isPublic: true;
startActivityOrFlow?: string | null;
publicAppletKey: string;
appletId?: undefined;
};

type PrivateAppletDetails = {
isPublic: false;
startActivityOrFlow?: string | null;
appletId: string;
publicAppletKey?: undefined;
};

type Props = PublicAppletDetails | PrivateAppletDetails;

export const ActivityGroups = (props: Props) => {
const { appletId, isPublic, publicAppletKey, startActivityOrFlow } = props;
const { t } = useCustomTranslation();
const navigate = useNavigate();
const location = useLocation();
Expand All @@ -39,8 +43,7 @@ export const ActivityGroups = (props: Props) => {
isOpen: false,
});
const { featureFlag } = useFeatureFlags();
const isAssignmentsEnabled =
featureFlag(FeatureFlag.EnableActivityAssign, false) && !props.isPublic;
const isAssignmentsEnabled = featureFlag(FeatureFlag.EnableActivityAssign, false) && !isPublic;

const {
isError: isAppletError,
Expand All @@ -60,7 +63,7 @@ export const ActivityGroups = (props: Props) => {
isError: isAssignmentsError,
isLoading: isAssignmentsLoading,
data: assignments = null,
} = useMyAssignmentsQuery(isAssignmentsEnabled ? props.appletId : undefined, {
} = useMyAssignmentsQuery(isAssignmentsEnabled ? appletId : undefined, {
select: (data) => data.data.result.assignments,
enabled: isAssignmentsEnabled,
});
Expand All @@ -74,7 +77,8 @@ export const ActivityGroups = (props: Props) => {

useOnceEffect(() => {
ensureMultiInformantStateExists();
if (isInMultiInformantFlow() && !props.startActivityOrFlow) {

if (isInMultiInformantFlow() && !startActivityOrFlow) {
resetMultiInformantState();
}

Expand All @@ -92,6 +96,15 @@ export const ActivityGroups = (props: Props) => {
}
});

useTakeNowRedirect({
activityOrFlowId: startActivityOrFlow,
applet,
assignments,
events: events?.events,
isPublic,
publicAppletKey,
});

if (isAppletLoading || isEventsLoading || (isAssignmentsEnabled && isAssignmentsLoading)) {
return <Loader />;
}
Expand Down

0 comments on commit 16dedf3

Please sign in to comment.