Skip to content

Commit

Permalink
Merge pull request #333 from cultuurnet/feature/UIT-209-welcome-rewar…
Browse files Browse the repository at this point in the history
…ds-filter

Welcome rewards filter
  • Loading branch information
chennara authored Dec 7, 2023
2 parents 65c5418 + 83b36cc commit 4d62a44
Show file tree
Hide file tree
Showing 15 changed files with 142 additions and 23 deletions.
Binary file added src/_assets/icons/chevronRight.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/_assets/icons/[email protected]
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/_assets/icons/[email protected]
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions src/_assets/icons/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export const CheckboxChecked = require('./checkboxChecked.png');
export const CheckboxUnchecked = require('./checkboxUnchecked.png');
export const ChevronDown = require('./chevronDown.png');
export const ChevronLeft = require('./chevronLeft.png');
export const ChevronRight = require('./chevronRight.png');
export const ChevronUp = require('./chevronUp.png');
export const Child = require('./child.png');
export const CircledCheck = require('./circledCheck.png');
Expand Down
74 changes: 74 additions & 0 deletions src/_components/family/familyFilter/FamilyFilter.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import { useRef } from 'react';
import { FlatList } from 'react-native';

import { useGetFamilyMembers } from '../../../onboarding/family/_queries';
import { TFamilyMember, TPassHolder } from '../../../profile/_models';
import Typography from '../../typography/Typography';
import * as Styled from './style';

type TProps = {
selectedPassHolder: TPassHolder;
setSelectedPassHolder: (selectedPassHolder: TPassHolder) => void;
};

export const FamilyFilter = ({ selectedPassHolder, setSelectedPassHolder }: TProps) => {
const listRef = useRef<FlatList>();

const { data: familyMembers = [] } = useGetFamilyMembers();

const handleSelectPassHolder = (nextSelectedPassHolder: TPassHolder, nextSelectedIndex: number) => {
if (selectedPassHolder.id !== nextSelectedPassHolder.id) {
setSelectedPassHolder(nextSelectedPassHolder);
listRef.current.scrollToIndex({ animated: true, index: nextSelectedIndex });
}
};

const handlePressPrev = () => {
const currentIndex = findCurrentIndexByPassHolder(familyMembers, selectedPassHolder);
const nextPassHolder = findBoundedPassHolderByIndex(familyMembers, currentIndex - 1);
setSelectedPassHolder(nextPassHolder);
};

const handleNextPrev = () => {
const currentIndex = findCurrentIndexByPassHolder(familyMembers, selectedPassHolder);
const nextPassHolder = findBoundedPassHolderByIndex(familyMembers, currentIndex + 1);
setSelectedPassHolder(nextPassHolder);
};

return (
<Styled.Container>
<Styled.FilterPrevButton name="ChevronLeft" onPress={handlePressPrev} />
<FlatList
ItemSeparatorComponent={Styled.Separator}
data={familyMembers}
horizontal
keyExtractor={item => item.uitpasNumber}
ref={listRef}
renderItem={({ item: familyMember, index }) => (
<Styled.FilterItem
isSelected={familyMember.passholder.id === selectedPassHolder.id}
onPress={() => handleSelectPassHolder(familyMember.passholder, index)}
>
<Typography
color={familyMember.passholder.id === selectedPassHolder.id ? 'neutral.0' : 'primary.100'}
fontStyle="bold"
size="small"
>
{familyMember.passholder.firstName}
</Typography>
</Styled.FilterItem>
)}
showsHorizontalScrollIndicator={false}
/>
<Styled.FilterNextButton name="ChevronRight" onPress={handleNextPrev} />
</Styled.Container>
);
};

const findBoundedPassHolderByIndex = (familyMembers: TFamilyMember[], index: number) => {
return familyMembers[Math.min(Math.max(index, 0), familyMembers.length - 1)].passholder;
};

const findCurrentIndexByPassHolder = (familyMembers: TFamilyMember[], passHolder: TPassHolder) => {
return familyMembers.findIndex(member => member.passholder.id === passHolder.id);
};
32 changes: 32 additions & 0 deletions src/_components/family/familyFilter/style.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import styled from 'styled-components/native';

import Icon from '../../icon/Icon';
import TouchableRipple from '../../touchableRipple/TouchableRipple';

export const Container = styled.View`
flex-direction: row;
align-items: center;
background-color: ${({ theme }) => theme.palette.primary[700]};
padding-vertical: 8px;
`;

export const FilterPrevButton = styled(Icon)`
margin-left: 3px;
margin-right: 8px;
`;

export const FilterNextButton = styled(Icon)`
margin-right: 3px;
margin-left: 8px;
`;

export const FilterItem = styled(TouchableRipple)<{ isSelected: boolean }>`
padding-horizontal: 16px;
padding-vertical: 4px;
background-color: ${({ isSelected, theme }) => (isSelected ? theme.palette.primary[900] : 'transparent')}};
border-radius: 20px;
`;

export const Separator = styled.View`
width: 8px;
`;
2 changes: 2 additions & 0 deletions src/_components/family/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './familyFilter/FamilyFilter';
export * from './familyMembersPoints/FamilyMembersPoints';
2 changes: 1 addition & 1 deletion src/_components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export { default as DatePicker } from './datePicker/DatePicker';
export { default as DiagonalSplitView } from './diagonalSplitView/DiagonalSplitView';
export { default as EnlargedHeader } from './enlargedHeader/EnlargedHeader';
export { default as ExternalLink } from './externalLink/ExternalLink';
export * from './family/familyMembersPoints/FamilyMembersPoints';
export * from './family';
export { default as FakeTextInput } from './textInput/fakeTextInput/FakeTextInput';
export { default as FocusAwareStatusBar } from './statusBar/FocusAwareStatusBar';
export { default as HtmlRenderer } from './htmlRenderer/HtmlRenderer';
Expand Down
2 changes: 1 addition & 1 deletion src/_routing/_components/TRootStackParamList.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { TFamilyMember } from '../../profile/_models';
import { TRedeemedReward } from '../../redeemedRewards/_models/redeemedReward';
import { TCheckInResponse } from '../../scan/_models';
import { TFamilyScanResponse } from '../../scan/familyCheckinSummary/_models';
import { TFilterRewardCategory, TFilterRewardSection } from '../../shop/_hooks/useRewardFilters';
import { TFilterRewardCategory, TFilterRewardSection } from '../../shop/_helpers/getRewardFilters';
import { TReward, TRewardType } from '../../shop/_models/reward';
import { TSearchFilters } from '../../shop/_models/searchFilters';
import { TFilterRewardSorting } from '../../shop/_queries/useGetRewards';
Expand Down
20 changes: 13 additions & 7 deletions src/filteredShop/FilteredShop.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import React from 'react';
import React, { useState } from 'react';
import { useTranslation } from 'react-i18next';
import { ActivityIndicator, RefreshControl } from 'react-native';
import { FlashList } from '@shopify/flash-list';

import { Analytics, Reward, RewardLoader } from '../_components';
import { Analytics, FamilyFilter, Reward, RewardLoader } from '../_components';
import { TRootStackNavigationProp, TRootStackRouteProp } from '../_routing';
import { theme } from '../_styles/theme';
import { normalizeForSlug } from '../_utils';
import { useRewardFilters } from '../shop/_hooks/useRewardFilters';
import { useGetMe } from '../profile/_queries/useGetMe';
import { getRewardFilters } from '../shop/_helpers/getRewardFilters';
import { useGetRewards } from '../shop/_queries/useGetRewards';
import { WelcomeHeader } from './_components/WelcomeHeader';
import * as Styled from './style';
Expand All @@ -22,14 +23,16 @@ const MINIMAL_REWARD_HEIGHT = 125;

export const FilteredShop = ({ route }: TProps) => {
const { subtitle, section, category, type } = route.params || {};
const { getFiltersForCategory, getFiltersForSection } = useRewardFilters();
const { t } = useTranslation();

const { data: me } = useGetMe();
const [selectedPassHolder, setSelectedPassHolder] = useState(me);
const { getFiltersForCategory, getFiltersForSection } = getRewardFilters({ passHolder: selectedPassHolder });
const {
data: rewards,
fetchNextPage,
isLoading: isRewardsLoading,
refetch,
isRefetching,
isFetchingNextPage,
} = useGetRewards({
filters: { type, ...getFiltersForSection(section), ...getFiltersForCategory(category) },
Expand Down Expand Up @@ -66,7 +69,10 @@ export const FilteredShop = ({ route }: TProps) => {
}
ListHeaderComponent={
isFilteredOnWelcome ? (
<WelcomeHeader />
<>
<FamilyFilter selectedPassHolder={selectedPassHolder} setSelectedPassHolder={setSelectedPassHolder} />
<WelcomeHeader />
</>
) : (
<Styled.Header fontStyle="bold" size="xxxlarge">
{subtitle}
Expand All @@ -83,7 +89,7 @@ export const FilteredShop = ({ route }: TProps) => {
<RefreshControl
colors={[theme.palette.primary['500'], theme.palette.neutral['0']]}
onRefresh={refetch}
refreshing={isRefetching}
refreshing={false} // Don't show the refresh control on loading new data, as this causes a layout shift when selecting another member in the list
tintColor={theme.palette.primary['500']}
/>
}
Expand Down
2 changes: 1 addition & 1 deletion src/shop/_components/categoryFilters/CategoryFilters.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { useStackNavigation } from '../../../_hooks';
import { TRootStackParamList } from '../../../_routing';
import { theme } from '../../../_styles/theme';
import { normalizeForSlug } from '../../../_utils';
import { TFilterRewardCategory, TFilterRewardSection } from '../../_hooks/useRewardFilters';
import { TFilterRewardCategory, TFilterRewardSection } from '../../_helpers/getRewardFilters';
import * as Styled from './style';

type TCategoryListItem = {
Expand Down
6 changes: 4 additions & 2 deletions src/shop/_components/rewardsSection/RewardsSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ import { REWARD_TILE_WIDTH } from '../../../_components/reward/style';
import { useTracking } from '../../../_context';
import { TMainNavigationProp } from '../../../_routing';
import { getRewardTrackingData } from '../../../_utils';
import { TFilterRewardCategory, TFilterRewardSection, useRewardFilters } from '../../_hooks/useRewardFilters';
import { useGetMe } from '../../../profile/_queries/useGetMe';
import { getRewardFilters, TFilterRewardCategory, TFilterRewardSection } from '../../_helpers/getRewardFilters';
import { useGetRewards } from '../../_queries/useGetRewards';
import { RewardsSectionLoader } from './RewardSection.loading';
import * as Styled from './style';
Expand Down Expand Up @@ -36,7 +37,8 @@ export const RewardsSection = ({
onLoaded,
...props
}: TRewardSectionProps) => {
const { getFiltersForCategory, getFiltersForSection } = useRewardFilters();
const { data: me } = useGetMe();
const { getFiltersForCategory, getFiltersForSection } = getRewardFilters({ passHolder: me });
const { data, isLoading } = useGetRewards({
filters: { ...getFiltersForSection(section), ...getFiltersForCategory(category) },
itemsPerPage: horizontal ? 20 : 3,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import { useGetMe } from '../../profile/_queries/useGetMe';
import { TPassHolder } from '../../profile/_models';
import { TRewardCategory } from '../_models/reward';
import { TSearchFilters } from '../_models/searchFilters';

type TProps = {
passHolder: TPassHolder;
};

export type TFilterRewardSection =
| 'online'
| 'in de kijker'
Expand All @@ -14,9 +18,7 @@ export type TFilterRewardSection =

export type TFilterRewardCategory = TRewardCategory | 'laatste kans' | 'forKids';

export function useRewardFilters() {
const { data: user } = useGetMe();

export function getRewardFilters({ passHolder }: TProps) {
const getFiltersForSection = (section: TFilterRewardSection): TSearchFilters => {
// filter for a section
switch (section) {
Expand All @@ -37,19 +39,19 @@ export function useRewardFilters() {
case 'interessant':
return {
includeAllCardSystems: true,
isInterestingForPassholderId: user?.id,
isInterestingForPassholderId: passHolder.id,
};
case 'populair regio':
return {};
case 'stad voordelen':
return {
includeAllCardSystems: true,
organizerPostalCode: user?.address?.postalCode,
organizerPostalCode: passHolder.address.postalCode,
};
case 'sport':
return { sport: true };
case 'welkom':
return { includeAllCardSystems: true, isRedeemableByPassholderId: user?.id, type: 'WELCOME' };
return { includeAllCardSystems: true, isRedeemableByPassholderId: passHolder.id, type: 'WELCOME' };
}
};

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { useCallback } from 'react';

import { FamilyMembersPoints } from '../../../_components/family/familyMembersPoints/FamilyMembersPoints';
import { FamilyMembersPoints } from '../../../_components';
import { useGetFamilyMembers } from '../../../onboarding/family/_queries';
import { TFamilyMember } from '../../../profile/_models';
import { TReward } from '../../../shop/_models/reward';
Expand Down
6 changes: 3 additions & 3 deletions src/shopDetail/_components/redeemModal/RedeemModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ const RedeemModal: FC<TRedeemModalProps> = ({ member, reward, isVisible, toggleI
navigate('RedeemedReward', { isModal: true, member, redeemedReward });
},
});
const firstActiveCard = getActiveCard({ passHolder: member?.passholder });
const activeCard = getActiveCard({ passHolder: member?.passholder });
const { trackSelfDescribingEvent } = useTracking();

useEffect(() => {
Expand All @@ -46,8 +46,8 @@ const RedeemModal: FC<TRedeemModalProps> = ({ member, reward, isVisible, toggleI

const handleConfirm = useCallback(() => {
trackSelfDescribingEvent('buttonClick', { button_name: 'redeem-confirm' }, { reward: rewardTrackingData });
redeemReward({ body: { rewardId: reward.id, uitpasNumber: firstActiveCard?.uitpasNumber } });
}, [reward, redeemReward, firstActiveCard?.uitpasNumber, trackSelfDescribingEvent, rewardTrackingData]);
redeemReward({ body: { rewardId: reward.id, uitpasNumber: activeCard?.uitpasNumber } });
}, [reward, redeemReward, activeCard?.uitpasNumber, trackSelfDescribingEvent, rewardTrackingData]);

const handleCancel = () => {
trackSelfDescribingEvent('buttonClick', { button_name: 'redeem-cancel' }, { reward: rewardTrackingData });
Expand Down

0 comments on commit 4d62a44

Please sign in to comment.