Skip to content

Commit e972ae0

Browse files
committed
fix: encounter tour
1 parent 59ebd46 commit e972ae0

File tree

5 files changed

+139
-117
lines changed

5 files changed

+139
-117
lines changed

mobile/components/OEncounter/OEncounter.tsx

Lines changed: 4 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,14 @@ import {
2020
import { useUserContext } from "@/context/UserContext";
2121
import { TR, i18n } from "@/localization/translate.service";
2222
import { ROUTES } from "@/screens/routes";
23-
import { MOCK_ENCOUNTER, TOURKEY } from "@/services/tourguide.service";
23+
import { TOURKEY } from "@/services/tourguide.service";
2424
import { TestData } from "@/tests/src/accessors";
2525
import { API } from "@/utils/api-config";
2626
import { getTimePassedWithText } from "@/utils/date.utils";
2727
import { getValidImgURI } from "@/utils/media.utils";
2828
import { MaterialIcons } from "@expo/vector-icons";
2929
import * as React from "react";
30-
import { useEffect, useState } from "react";
30+
import { useState } from "react";
3131
import { Image, StyleSheet, Text, View } from "react-native";
3232
import { Dropdown } from "react-native-element-dropdown";
3333
import { TouchableOpacity } from "react-native-gesture-handler";
@@ -114,73 +114,10 @@ const OEncounter = (props: ISingleEncounterProps) => {
114114
encounterProfile.otherUser.id,
115115
);
116116

117-
const handleTourOnStepChange = (e: any) => {
118-
if (e?.order === 2) {
119-
dispatch({
120-
type: EACTION_ENCOUNTERS.PUSH_MULTIPLE,
121-
payload: [MOCK_ENCOUNTER({ status: "met_interested" })],
122-
});
123-
} else if (e?.order === 3) {
124-
dispatch({
125-
type: EACTION_ENCOUNTERS.PUSH_MULTIPLE,
126-
payload: [
127-
MOCK_ENCOUNTER({
128-
status: "met_interested",
129-
messages: [
130-
{
131-
id: "44",
132-
content:
133-
"Forgot to drop my number :), +43 xxxxxxx",
134-
senderUserId: "abc",
135-
sentAt: new Date().toISOString(),
136-
},
137-
],
138-
}),
139-
],
140-
});
141-
} else if (e?.order === 4) {
142-
dispatch({
143-
type: EACTION_ENCOUNTERS.PUSH_MULTIPLE,
144-
payload: [MOCK_ENCOUNTER({ status: "met_not_interested" })],
145-
});
146-
} else if (e?.order === 5) {
147-
dispatch({
148-
type: EACTION_ENCOUNTERS.PUSH_MULTIPLE,
149-
payload: [MOCK_ENCOUNTER({ status: "not_met" })],
150-
});
151-
} else if (e?.order === 6) {
152-
// @dev revert to order4 state "previous" button would otherwise keep the 7th state.
153-
dispatch({
154-
type: EACTION_ENCOUNTERS.PUSH_MULTIPLE,
155-
payload: [MOCK_ENCOUNTER({ status: "not_met" })],
156-
});
157-
} else if (e?.order === 7) {
158-
dispatch({
159-
type: EACTION_ENCOUNTERS.PUSH_MULTIPLE,
160-
payload: [
161-
MOCK_ENCOUNTER({
162-
status: "met_interested",
163-
isNearbyRightNow: true,
164-
}),
165-
],
166-
});
167-
}
168-
};
169-
170-
const { tourKey, eventEmitter, stop } = useTourGuideController(
117+
const { tourKey, stop: stopTour } = useTourGuideController(
171118
TOURKEY.ENCOUNTERS,
172119
);
173120

174-
useEffect(() => {
175-
if (!eventEmitter) return;
176-
eventEmitter?.on("stepChange", handleTourOnStepChange);
177-
178-
return () => {
179-
eventEmitter?.off("stepChange", handleTourOnStepChange);
180-
};
181-
// @dev Keep mapRegion in dependency to mock heatmap along current mapRegion
182-
}, [eventEmitter]);
183-
184121
return (
185122
<TourGuideZone
186123
zone={1}
@@ -192,7 +129,7 @@ const OEncounter = (props: ISingleEncounterProps) => {
192129
<View style={styles.mainContent}>
193130
<TouchableOpacity
194131
onPress={() => {
195-
stop(); // @dev Stop tourguide when screen changes.
132+
stopTour(); // @dev Stop tourguide when screen changes.
196133
navigation.navigate(ROUTES.Main.ProfileView, {
197134
user: encounterProfile.otherUser,
198135
});

mobile/components/OEncounterList/OEncounterList.tsx

Lines changed: 31 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,8 @@ import {
66
} from "@/context/EncountersContext";
77
import { useUserContext } from "@/context/UserContext";
88
import { TR, i18n } from "@/localization/translate.service";
9-
import {
10-
LOCAL_VALUE,
11-
getLocalValue,
12-
saveLocalValue,
13-
} from "@/services/storage.service";
14-
import { MOCK_ENCOUNTER, TOURKEY } from "@/services/tourguide.service";
9+
import { LOCAL_VALUE, saveLocalValue } from "@/services/storage.service";
10+
import { TOURKEY } from "@/services/tourguide.service";
1511
import { API } from "@/utils/api-config";
1612
import { useIsFocused, useNavigation } from "@react-navigation/native";
1713
import * as Sentry from "@sentry/react-native";
@@ -25,6 +21,7 @@ import {
2521
View,
2622
} from "react-native";
2723
import { useTourGuideController } from "rn-tourguide";
24+
import { OTourEncounter } from "../OTourEncounter/OTourEncounter";
2825

2926
interface IOEncounterListProps {
3027
metStartDateFilter: Date;
@@ -83,15 +80,15 @@ export const OEncounterList = (props: IOEncounterListProps) => {
8380
LOCAL_VALUE.HAS_DONE_ENCOUNTER_WALKTHROUGH,
8481
"true",
8582
);
86-
await fetchEncounters(); // @dev clear mocked encounters
83+
dispatch({
84+
type: EACTION_ENCOUNTERS.SET_IS_WALKTHROUGH_RUNNING,
85+
payload: false,
86+
});
8787
};
8888

89-
const {
90-
canStart,
91-
start,
92-
stop: stopTourGuide,
93-
eventEmitter,
94-
} = useTourGuideController(TOURKEY.ENCOUNTERS);
89+
const { stop: stopTourGuide, eventEmitter } = useTourGuideController(
90+
TOURKEY.ENCOUNTERS,
91+
);
9592

9693
useEffect(() => {
9794
if (!eventEmitter) return;
@@ -100,7 +97,6 @@ export const OEncounterList = (props: IOEncounterListProps) => {
10097
return () => {
10198
eventEmitter?.off("stop", handleTourOnStop);
10299
};
103-
// @dev Keep mapRegion in dependency to mock heatmap along current mapRegion
104100
}, [eventEmitter, refreshing]);
105101

106102
const isFocused = useIsFocused();
@@ -110,35 +106,11 @@ export const OEncounterList = (props: IOEncounterListProps) => {
110106
}
111107
}, [isFocused]);
112108

113-
useEffect(() => {
114-
getLocalValue(LOCAL_VALUE.HAS_DONE_ENCOUNTER_WALKTHROUGH).then(
115-
(hasDoneWalkthrough) => {
116-
if (hasDoneWalkthrough?.toLowerCase().trim() === "true") return;
117-
if (!refreshing) {
118-
dispatch({
119-
type: EACTION_ENCOUNTERS.PUSH_MULTIPLE,
120-
payload: [MOCK_ENCOUNTER(null)],
121-
});
122-
if (canStart) {
123-
start();
124-
}
125-
}
126-
},
127-
);
128-
}, [canStart, refreshing]);
129-
130-
return (
131-
<ScrollView
132-
style={styles.encountersList}
133-
refreshControl={
134-
<RefreshControl refreshing={refreshing} onRefresh={onRefresh} />
135-
}
136-
contentContainerStyle={
137-
encounterState.encounters.length === 0 &&
138-
styles.emptyListContainer
139-
}
140-
>
141-
{encounterState.encounters.length > 0 ? (
109+
const getEncounterList = () => {
110+
if (encounterState.isWalkthroughRunning) {
111+
return <OTourEncounter />;
112+
} else {
113+
return encounterState.encounters.length > 0 ? (
142114
encounterState.encounters.map((encounter, idx) => (
143115
<OEncounter
144116
key={idx}
@@ -156,7 +128,22 @@ export const OEncounterList = (props: IOEncounterListProps) => {
156128
{i18n.t(TR.nobodyWasNearbySubtitle)}
157129
</Text>
158130
</View>
159-
)}
131+
);
132+
}
133+
};
134+
135+
return (
136+
<ScrollView
137+
style={styles.encountersList}
138+
refreshControl={
139+
<RefreshControl refreshing={refreshing} onRefresh={onRefresh} />
140+
}
141+
contentContainerStyle={
142+
encounterState.encounters.length === 0 &&
143+
styles.emptyListContainer
144+
}
145+
>
146+
{getEncounterList()}
160147
</ScrollView>
161148
);
162149
};
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
import { EncounterPublicDTO } from "@/api/gen/src";
2+
import OEncounter from "@/components/OEncounter/OEncounter";
3+
import { MOCK_ENCOUNTER, TOURKEY } from "@/services/tourguide.service";
4+
import * as React from "react";
5+
import { useCallback, useEffect, useRef, useState } from "react";
6+
import { useTourGuideController } from "rn-tourguide";
7+
8+
/// @dev For cleaner code and due to render/component lifecycle issues.
9+
export const OTourEncounter = ({ navigation }: any) => {
10+
const tourInitialized = useRef(false);
11+
const [encounter, setEncounter] = useState<EncounterPublicDTO>(
12+
MOCK_ENCOUNTER(null),
13+
);
14+
const handleTourOnStepChange = useCallback(
15+
(e: any) => {
16+
if (e?.order === 2) {
17+
setEncounter(MOCK_ENCOUNTER({ status: "met_interested" }));
18+
} else if (e?.order === 3) {
19+
setEncounter(
20+
MOCK_ENCOUNTER({
21+
status: "met_interested",
22+
messages: [
23+
{
24+
id: "44",
25+
content:
26+
"Forgot to drop my number :), +43 xxxxxxx",
27+
senderUserId: "abc",
28+
sentAt: new Date().toISOString(),
29+
},
30+
],
31+
}),
32+
);
33+
} else if (e?.order === 4) {
34+
setEncounter(MOCK_ENCOUNTER({ status: "met_not_interested" }));
35+
} else if (e?.order === 5) {
36+
setEncounter(MOCK_ENCOUNTER({ status: "not_met" }));
37+
} else if (e?.order === 6) {
38+
// @dev revert to order4 state "previous" button would otherwise keep the 7th state.
39+
setEncounter(MOCK_ENCOUNTER({ status: "not_met" }));
40+
} else if (e?.order === 7) {
41+
setEncounter(
42+
MOCK_ENCOUNTER({
43+
status: "met_interested",
44+
isNearbyRightNow: true,
45+
}),
46+
);
47+
}
48+
},
49+
[setEncounter],
50+
);
51+
52+
const {
53+
eventEmitter,
54+
canStart: canStartTourEncounters,
55+
start: startTourEncounters,
56+
} = useTourGuideController(TOURKEY.ENCOUNTERS);
57+
58+
useEffect(() => {
59+
if (!eventEmitter) return;
60+
eventEmitter?.on("stepChange", handleTourOnStepChange);
61+
62+
return () => {
63+
eventEmitter?.off("stepChange", handleTourOnStepChange);
64+
};
65+
// @dev Keep mapRegion in dependency to mock heatmap along current mapRegion
66+
}, [eventEmitter]);
67+
68+
useEffect(() => {
69+
if (canStartTourEncounters && !tourInitialized.current) {
70+
requestAnimationFrame(() => {
71+
startTourEncounters();
72+
tourInitialized.current = true;
73+
});
74+
}
75+
}, [canStartTourEncounters, startTourEncounters, tourInitialized]);
76+
77+
return (
78+
<OEncounter
79+
encounterProfile={encounter}
80+
showActions={true}
81+
navigation={navigation}
82+
/>
83+
);
84+
};

mobile/context/EncountersContext.tsx

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import React, { Dispatch, createContext, useContext, useReducer } from "react";
33

44
export interface IEncounters {
55
encounters: EncounterPublicDTO[];
6+
isWalkthroughRunning: boolean;
67
}
78

89
/** @dev Partial Encounter BUT id is mandatory since the update logic needs it as we are saving an array */
@@ -13,6 +14,7 @@ export type PartialEncounterProfile = {
1314
export enum EACTION_ENCOUNTERS {
1415
UPDATE_MULTIPLE = "UPDATE_MULTIPLE",
1516
PUSH_MULTIPLE = "PUSH_MULTIPLE",
17+
SET_IS_WALKTHROUGH_RUNNING = "SET_IS_WALKTHROUGH_RUNNING",
1618
}
1719

1820
interface IEncountersContextType {
@@ -22,11 +24,12 @@ interface IEncountersContextType {
2224

2325
export interface IEncountersAction {
2426
type: EACTION_ENCOUNTERS;
25-
payload: PartialEncounterProfile[];
27+
payload: PartialEncounterProfile[] | boolean;
2628
}
2729

2830
const initialState: IEncounters = {
2931
encounters: [],
32+
isWalkthroughRunning: false,
3033
};
3134

3235
export const getPublicProfileFromEncounter = (
@@ -46,6 +49,12 @@ const userReducer = (
4649
action: IEncountersAction,
4750
): IEncounters => {
4851
switch (action.type) {
52+
case EACTION_ENCOUNTERS.SET_IS_WALKTHROUGH_RUNNING:
53+
return {
54+
...state,
55+
isWalkthroughRunning: action.payload as boolean,
56+
};
57+
4958
case EACTION_ENCOUNTERS.PUSH_MULTIPLE:
5059
const fetchedEncounters = action.payload as EncounterPublicDTO[];
5160
/** @DEV TODO */
@@ -57,7 +66,7 @@ const userReducer = (
5766

5867
case EACTION_ENCOUNTERS.UPDATE_MULTIPLE:
5968
const payload: PartialEncounterProfile[] =
60-
action.payload satisfies PartialEncounterProfile[];
69+
action.payload as PartialEncounterProfile[];
6170

6271
const currentEncounters = state.encounters;
6372

mobile/screens/main/MainScreenTabs.tsx

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import { useNotifications } from "@/hooks/useNotifications";
99
import { TR, i18n } from "@/localization/translate.service";
1010
import { MainTabs } from "@/screens/main/MainScreenTabs.navigator";
1111
import { LOCAL_VALUE, getLocalValue } from "@/services/storage.service";
12-
import { MOCK_ENCOUNTER, TOURKEY } from "@/services/tourguide.service";
12+
import { TOURKEY } from "@/services/tourguide.service";
1313
import { MaterialIcons } from "@expo/vector-icons";
1414
import * as Sentry from "@sentry/react-native";
1515
import * as React from "react";
@@ -23,14 +23,19 @@ import ProfileSettings from "./ProfileSettings";
2323
export const MainScreenTabs = ({ navigation }: any) => {
2424
useNotifications(navigation);
2525

26-
const { dispatch: dispatchEncounters } = useEncountersContext();
26+
const { state: encounterState, dispatch: dispatchEncounters } =
27+
useEncountersContext();
2728
const { tourKey: tourKeyFind, start: startTourFind } =
2829
useTourGuideController(TOURKEY.FIND);
30+
const { start: startTourEncounters } = useTourGuideController(
31+
TOURKEY.ENCOUNTERS,
32+
);
2933

3034
// @dev true by default to not unnecessarily distract user
3135
const [hasDoneFindWalkthrough, setHasDoneFindWalkthrough] = useState(true);
3236
const [hasDoneEncounterWalkthrough, setHasDoneEncounterWalkthrough] =
3337
useState(true);
38+
3439
useEffect(() => {
3540
const getValues = [
3641
getLocalValue(LOCAL_VALUE.HAS_DONE_FIND_WALKTHROUGH),
@@ -111,8 +116,8 @@ export const MainScreenTabs = ({ navigation }: any) => {
111116
highlightHelpBtn={!hasDoneEncounterWalkthrough}
112117
onHelpPress={() => {
113118
dispatchEncounters({
114-
type: EACTION_ENCOUNTERS.PUSH_MULTIPLE,
115-
payload: [MOCK_ENCOUNTER(null)],
119+
type: EACTION_ENCOUNTERS.SET_IS_WALKTHROUGH_RUNNING,
120+
payload: true,
116121
});
117122
}}
118123
/>

0 commit comments

Comments
 (0)