Skip to content

Commit

Permalink
[expo] implement UPFC tab
Browse files Browse the repository at this point in the history
**Summary**

UPFC Tab shows the list of the current applications and remind users that they have payment due for their applications.

**Test**

- expo

**Issue**

- N/A
  • Loading branch information
yssk22 committed Jun 25, 2024
1 parent 362417d commit da28ed0
Show file tree
Hide file tree
Showing 20 changed files with 487 additions and 41 deletions.
7 changes: 5 additions & 2 deletions expo/assets/translations.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,11 @@
"Artists": {
"ja": "アーティスト"
},
"Events": {
"ja": "イベント"
"Fan Club": {
"ja": "ファンクラブ"
},
"Configure fan club settings": {
"ja": "ファンクラブ設定を行う"
},
"Goods": {
"ja": "グッズ"
Expand Down
65 changes: 65 additions & 0 deletions expo/features/common/components/card/Card.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import Text from '@hpapp/features/common/components/Text';
import { FontSize, Spacing } from '@hpapp/features/common/constants';
import { ColorScheme, useColor } from '@hpapp/features/settings/context/theme';
import React from 'react';
import { View, StyleSheet } from 'react-native';

type StyleProps = React.ComponentProps<typeof View>['style'];

export type CardProps = {
colorScheme?: ColorScheme;
headerText?: string;
subHeaderText?: string;
footerContent?: JSX.Element;
containerStyle?: StyleProps;
headerStyle?: StyleProps;
children: React.ReactNode;
};

export default function Card({
colorScheme = 'primary',
headerText,
subHeaderText,
containerStyle,
headerStyle,
children
}: CardProps) {
const [color, contrast] = useColor(colorScheme);
const showHeader = headerText !== undefined || subHeaderText !== undefined;
return (
<View style={[styles.container, { borderColor: color }, containerStyle]}>
{showHeader && (
<View style={[styles.header, { backgroundColor: color }, headerStyle]}>
{headerText && (
<Text style={[{ color: contrast }, styles.headerText]} numberOfLines={1}>
{headerText}
</Text>
)}
{subHeaderText && (
<Text style={[{ color: contrast }, styles.subHeaderText]} numberOfLines={1}>
{subHeaderText}
</Text>
)}
</View>
)}
<View>{children}</View>
</View>
);
}

const styles = StyleSheet.create({
container: {
borderRadius: 3,
borderWidth: 1
},
header: {
padding: Spacing.Small
},
headerText: {
fontSize: FontSize.Medium,
fontWeight: 'bold'
},
subHeaderText: {
fontSize: FontSize.XXSmall
}
});
19 changes: 19 additions & 0 deletions expo/features/common/components/card/CardBody.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { Spacing } from '@hpapp/features/common/constants';
import { View, StyleSheet } from 'react-native';

type StyleProps = React.ComponentProps<typeof View>['style'];

export type CardBodyProps = {
style?: StyleProps;
children: React.ReactNode;
};

export default function CardBody({ style, children }: CardBodyProps) {
return <View style={[styles.container, style]}>{children}</View>;
}

const styles = StyleSheet.create({
container: {
padding: Spacing.Small
}
});
31 changes: 31 additions & 0 deletions expo/features/common/components/card/CardSkelton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { Spacing } from '@hpapp/features/common/constants';
import { ColorScheme, useSkeltonColor } from '@hpapp/features/settings/context/theme';
import ContentLoader, { Rect } from 'react-content-loader/native';

type CardSkeltonProps = {
color?: ColorScheme;
};

export default function CardSkelton({ color = 'primary' }: CardSkeltonProps) {
const primary = useSkeltonColor(color);
const innerBarX = Spacing.Small + 3 + Spacing.Medium + 50 + Spacing.Medium;
const innerBarWidth = 400 - (Spacing.Small + 3 + Spacing.Medium + 50 + Spacing.Medium * 3);
return (
<ContentLoader speed={2} width="100%" height="225" viewBox="0 0 400 225" backgroundColor={primary}>
<Rect x={Spacing.Small} y="0" rx="0" ry="0" width={400 - 2 * Spacing.Small} height="45" />
<Rect x={Spacing.Small} y="225" rx="0" ry="0" width={400 - 2 * Spacing.Small} height="3" />

<Rect x={Spacing.Small} y="45" rx="0" ry="0" width="3" height="180" />
<Rect x={400 - Spacing.Small - 3} y="45" rx="0" ry="0" width="3" height="180" />

<Rect x={Spacing.Small + 3 + Spacing.Medium} y="65" rx="0" ry="0" width="50" height="50" />
<Rect x={Spacing.Small + 3 + Spacing.Medium} y="145" rx="0" ry="0" width="50" height="50" />

<Rect x={innerBarX} y="75" rx="0" ry="0" width={innerBarWidth} height="5" />
<Rect x={innerBarX} y="100" rx="0" ry="0" width={innerBarWidth} height="5" />

<Rect x={innerBarX} y="155" rx="0" ry="0" width={innerBarWidth} height="5" />
<Rect x={innerBarX} y="180" rx="0" ry="0" width={innerBarWidth} height="5" />
</ContentLoader>
);
}
18 changes: 0 additions & 18 deletions expo/features/home/Events.tsx

This file was deleted.

6 changes: 3 additions & 3 deletions expo/features/home/HomeScreen.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import ArtistsTab from '@hpapp/features/artist/ArtistsTab';
import Loading from '@hpapp/features/common/components/Loading';
import EventsTab from '@hpapp/features/home/Events';
import GoodsTab from '@hpapp/features/home/GoodsTab';
import HomeTab from '@hpapp/features/home/HomeTab';
import AppUpdateBanner from '@hpapp/features/root/banner/AppUpdateBanner';
import { defineScreen, useNavigationOption } from '@hpapp/features/root/protected/stack';
import SettingsTab from '@hpapp/features/settings/SettingsTab';
import { useColor } from '@hpapp/features/settings/context/theme';
import UPFCTab from '@hpapp/features/upfc/UPFCTab';
import { UPFCProvider } from '@hpapp/features/upfc/context';
import { t } from '@hpapp/system/i18n';
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
Expand Down Expand Up @@ -35,8 +35,8 @@ const Tabs: TabSpec[] = [
icon: 'people'
},
{
name: 'Events',
component: EventsTab,
name: 'Fan Club',
component: UPFCTab,
icon: 'calendar'
},
{
Expand Down
29 changes: 22 additions & 7 deletions expo/features/settings/context/theme.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -212,16 +212,22 @@ const useColor = (scheme: ColorScheme) => {
return [color, yiq > yiqThreshod ? theme.colors.white : theme.colors.black];
};

const useSkeltonColor = (scheme: ColorScheme, percent = 0.8) => {
const { theme } = useTheme();
const color = theme.colors[scheme];
const [r, g, b] = colorToRGB(color);
const r1 = Math.min(255, Math.floor(r + (255 - r) * percent));
const g1 = Math.min(255, Math.floor(g + (255 - g) * percent));
const b1 = Math.min(255, Math.floor(b + (255 - b) * percent));
// convert r1, g1, b1 to hex
return `#${r1.toString(16).padStart(2, '0')}${g1.toString(16).padStart(2, '0')}${b1.toString(16).padStart(2, '0')}`;
};

const yiqThreshod = 64;
// https://en.wikipedia.org/wiki/YIQ
const calcYIQ = (color: string) => {
// #aabbcc => [R, G, B] as numbers
const [r, g, b] = color
.toUpperCase()
.split('')
.slice(1, 7)
.reduce((acc: string[], curr, n, arr) => (n % 2 ? [...acc, `${arr[n - 1]}${curr}`] : acc), [])
.map((h) => parseInt(h, 16));
const [r, g, b] = colorToRGB(color);
return (r * 299 + g * 587 + b * 114) / 1000;
};

Expand Down Expand Up @@ -249,4 +255,13 @@ const rgbToColor = (rgb: string): ColorDef | undefined => {
return AvailableColorsRGBToColor[rgb];
};

export { AppThemeProvider, useAppTheme, useColor, ColorScheme, ColorKey, AvailableColors };
function colorToRGB(color: string) {
return color
.toUpperCase()
.split('')
.slice(1, 7)
.reduce((acc: string[], curr, n, arr) => (n % 2 ? [...acc, `${arr[n - 1]}${curr}`] : acc), [])
.map((h) => parseInt(h, 16));
}

export { AppThemeProvider, useAppTheme, useColor, useSkeltonColor, ColorScheme, ColorKey, AvailableColors };
109 changes: 109 additions & 0 deletions expo/features/upfc/UPFCApplicationCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
import CalendarDateIcon from '@hpapp/features/common/components/CalendarDateIcon';
import Text from '@hpapp/features/common/components/Text';
import Card from '@hpapp/features/common/components/card/Card';
import ListItem from '@hpapp/features/common/components/list/ListItem';
import { FontSize, IconSize, Spacing } from '@hpapp/features/common/constants';
import { useColor } from '@hpapp/features/settings/context/theme';
import UPFCApplicationStatusLabel from '@hpapp/features/upfc/UPFCApplicationStatusLabel';
import { EventApplicationTickets } from '@hpapp/features/upfc/scraper';
import useUPFCWebView from '@hpapp/features/upfc/useUPFCWebView';
import { toDateString, toTimeString } from '@hpapp/foundation/date';
import { Button, Icon } from '@rneui/themed';
import { useMemo } from 'react';
import { View, StyleSheet } from 'react-native';

const styles = StyleSheet.create({
card: {
margin: Spacing.XSmall
},
listItem: {
paddingTop: Spacing.XXSmall,
paddingBottom: Spacing.XSmall,
paddingLeft: Spacing.XXSmall,
paddingRight: Spacing.XSmall
},
listItemCenter: {
justifyContent: 'space-around',
paddingLeft: Spacing.Small,
paddingRight: Spacing.Small,
flexGrow: 1
},
listItemCenterText: {
fontSize: FontSize.Small
},
footer: {
flexGrow: 1,
justifyContent: 'flex-end',
alignItems: 'flex-end',
marginTop: Spacing.Small,
paddingRight: Spacing.Small,
paddingBottom: Spacing.Small
},
footerButtonIcon: {
marginLeft: Spacing.Small
}
});

export type UPFCApplicationCardProps = {
event: EventApplicationTickets;
};

export default function UPFCApplicationCard({ event }: UPFCApplicationCardProps) {
const [color] = useColor('primary');
const openUPFCWeView = useUPFCWebView();
const paymentWindowString = `${toDateString(event.paymentOpenDate)} ~ ${toDateString(event.paymentDueDate)}`;
const items = useMemo(() => {
return event.tickets.map((t) => {
return (
<ListItem
containerStyle={styles.listItem}
key={`${event.name}.${t.venue}.${t.startAt.getTime()}`}
leftContent={<CalendarDateIcon date={t.startAt} />}
rightContent={<UPFCApplicationStatusLabel ticket={t} />}
>
<View style={styles.listItemCenter}>
<Text style={styles.listItemCenterText} numberOfLines={1}>
{t.venue}
</Text>
<Text style={styles.listItemCenterText}>{`開場: ${toTimeString(t.openAt)} 開演:${toTimeString(
t.startAt
)}`}</Text>
</View>
</ListItem>
);
});
}, [event]);
const footer = useMemo(() => {
if (event.paymentDueDate) {
return (
<View style={styles.footer}>
<Button
color="secondary"
type="outline"
onPress={() => {
openUPFCWeView({
urlParams: 'Contents=MYPAGE02'
});
}}
>
{paymentWindowString}
<Icon
style={styles.footerButtonIcon}
type="antdesign"
name="pay-circle1"
size={IconSize.Small}
color={color}
/>
</Button>
</View>
);
}
return null;
}, [event.paymentDueDate]);
return (
<Card containerStyle={styles.card} headerText={event.name}>
{items}
{footer}
</Card>
);
}
42 changes: 42 additions & 0 deletions expo/features/upfc/UPFCApplicationStatusLabel.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import Text from '@hpapp/features/common/components/Text';
import { FontSize, Spacing } from '@hpapp/features/common/constants';
import { ColorScheme, useColor } from '@hpapp/features/settings/context/theme';
import { EventTicket } from '@hpapp/features/upfc/scraper';
import React from 'react';
import { View, StyleSheet } from 'react-native';

const StatusColorScheme: Record<string, ColorScheme> = {
申込済: 'success',
入金待: 'error',
入金済: 'primary',
入金忘: 'disabled',
落選: 'disabled',
不明: 'warning'
};

export type UPFCApplicationStatusLabelProps = {
ticket: EventTicket;
};

export default function UPFCApplicationStatusLabel({ ticket }: UPFCApplicationStatusLabelProps) {
const [color, contrast] = useColor(StatusColorScheme[ticket.status]);
return (
<View style={[styles.container, { backgroundColor: color }]}>
<Text style={[styles.text, { color: contrast }]}>{ticket.status}</Text>
</View>
);
}

const styles = StyleSheet.create({
container: {
paddingLeft: Spacing.XSmall,
paddingRight: Spacing.XSmall,
paddingTop: Spacing.XXSmall,
paddingBottom: Spacing.XXSmall,
width: 50,
alignItems: 'center'
},
text: {
fontSize: FontSize.XSmall
}
});
Loading

0 comments on commit da28ed0

Please sign in to comment.