diff --git a/app/_layout.tsx b/app/_layout.tsx index cb931c3..05a33e4 100644 --- a/app/_layout.tsx +++ b/app/_layout.tsx @@ -25,7 +25,7 @@ const AppStack = (): JSX.Element => { fontWeight: 'bold', }, }}> - + ); }; diff --git a/app/index.tsx b/app/index.tsx index daf4412..ac049bb 100644 --- a/app/index.tsx +++ b/app/index.tsx @@ -10,10 +10,11 @@ import {BadgeForm} from '@/components/BadgeForm'; import {BadgeScanning} from '@/components/BadgeScanning'; import {type BadgeConfigFormData} from '@/models/BadgeForm.model'; import {type BadgeMagic} from '@/models/BadgeMagic.model'; +import {Animations} from '@/utils/animations'; import {sendPackets} from '@/utils/bluetooth'; import {getPackets} from '@/utils/payload'; -const DefaultFormData: BadgeConfigFormData = { +const DefaultBagdeConfigFormData: BadgeConfigFormData = { text: '', effects: { flash: false, @@ -21,13 +22,14 @@ const DefaultFormData: BadgeConfigFormData = { invertLed: false, }, speed: 0, + animation: Animations.RIGHT, }; const Home = (): JSX.Element => { const [scanning, setScanning] = useState(false); const [connectedBadge, setConnectedBadge] = useState(); - const methods = useForm({defaultValues: DefaultFormData}); + const methods = useForm({defaultValues: DefaultBagdeConfigFormData}); const handleSendToBadge = async (data: BadgeConfigFormData): Promise => { if (!connectedBadge) { @@ -79,7 +81,7 @@ const styles = StyleSheet.create({ position: 'absolute', bottom: 40, gap: 8, - left: 20, + left: 40, alignItems: 'center', justifyContent: 'space-between', flexDirection: 'row', diff --git a/src/assets/ic_anim_animation.gif b/src/assets/ic_anim_animation.gif new file mode 100644 index 0000000..f82b328 Binary files /dev/null and b/src/assets/ic_anim_animation.gif differ diff --git a/src/assets/ic_anim_down.gif b/src/assets/ic_anim_down.gif new file mode 100644 index 0000000..8e0d504 Binary files /dev/null and b/src/assets/ic_anim_down.gif differ diff --git a/src/assets/ic_anim_fixed.gif b/src/assets/ic_anim_fixed.gif new file mode 100644 index 0000000..4589377 Binary files /dev/null and b/src/assets/ic_anim_fixed.gif differ diff --git a/src/assets/ic_anim_laser.gif b/src/assets/ic_anim_laser.gif new file mode 100644 index 0000000..9351360 Binary files /dev/null and b/src/assets/ic_anim_laser.gif differ diff --git a/src/assets/ic_anim_left.gif b/src/assets/ic_anim_left.gif new file mode 100644 index 0000000..e8113cd Binary files /dev/null and b/src/assets/ic_anim_left.gif differ diff --git a/src/assets/ic_anim_picture.gif b/src/assets/ic_anim_picture.gif new file mode 100644 index 0000000..e68dd13 Binary files /dev/null and b/src/assets/ic_anim_picture.gif differ diff --git a/src/assets/ic_anim_right.gif b/src/assets/ic_anim_right.gif new file mode 100644 index 0000000..9dd3583 Binary files /dev/null and b/src/assets/ic_anim_right.gif differ diff --git a/src/assets/ic_anim_up.gif b/src/assets/ic_anim_up.gif new file mode 100644 index 0000000..076f234 Binary files /dev/null and b/src/assets/ic_anim_up.gif differ diff --git a/src/components/Animations.tsx b/src/components/Animations.tsx index e69de29..c83bc9a 100644 --- a/src/components/Animations.tsx +++ b/src/components/Animations.tsx @@ -0,0 +1,35 @@ +import {StyleSheet, View} from 'react-native'; + +import {useFormContext} from 'react-hook-form'; + +import {animations} from '@/utils/animations'; + +import {ControlledCard} from './CardControlled'; + +export const Animations = (): JSX.Element => { + const {control} = useFormContext(); + + return ( + + {animations.map((animation) => ( + + ))} + + ); +}; + +const styles = StyleSheet.create({ + cardsContainer: { + paddingTop: 16, + flexDirection: 'row', + flexWrap: 'wrap', + justifyContent: 'space-evenly', + }, +}); diff --git a/src/components/BadgeForm.tsx b/src/components/BadgeForm.tsx index 374d17d..0dcbf20 100644 --- a/src/components/BadgeForm.tsx +++ b/src/components/BadgeForm.tsx @@ -1,5 +1,5 @@ import {useState} from 'react'; -import {StyleSheet, View, useWindowDimensions, Text} from 'react-native'; +import {StyleSheet, View, useWindowDimensions} from 'react-native'; import {useFormContext} from 'react-hook-form'; import {SceneMap, TabView} from 'react-native-tab-view'; @@ -8,12 +8,11 @@ import {AppInput} from '@/components/AppInput'; import {tabRoutes} from '@/utils/tabRoutes'; import {type BadgeConfigFormData} from '../models/BadgeForm.model'; +import {Animations} from './Animations'; import {BadgeConfigTabBar} from './BadgeConfigTabBar'; import {Effects} from './Effects'; import {SpeedSlider} from './Speed'; -const Animations = (): JSX.Element => Nothing implemented 00; - const renderScene = SceneMap({ effects: Effects, animations: Animations, diff --git a/src/components/CardControlled.tsx b/src/components/CardControlled.tsx index 186bcdf..7bf083f 100644 --- a/src/components/CardControlled.tsx +++ b/src/components/CardControlled.tsx @@ -3,34 +3,45 @@ import {type ImageSourcePropType, Text, StyleSheet} from 'react-native'; import {type FieldValues, type UseControllerProps, useController} from 'react-hook-form'; import {useTheme, Card} from 'react-native-paper'; +import {type Animations} from '@/utils/animations'; + type ControlledCardProps = { imagePath: ImageSourcePropType; title: string; + code?: Animations; } & UseControllerProps; export const ControlledCard = ({ control, name, + code, title, imagePath, }: ControlledCardProps): JSX.Element => { const {colors} = useTheme(); + const isAnimationSelected = name === 'animation'; const { field: {value, onChange}, } = useController({name, control}); const handleOnPress = (): void => { - onChange(!value); + onChange(isAnimationSelected ? code : !value); }; + const cardColor = isAnimationSelected + ? value === code + ? colors.primary + : colors.onPrimary + : value + ? colors.primary + : colors.onPrimary; + return ( - + - {title} + {title} ); @@ -38,8 +49,7 @@ export const ControlledCard = ({ const styles = StyleSheet.create({ card: { - flex: 1, - margin: 10, + marginTop: 10, alignItems: 'center', justifyContent: 'center', borderRadius: 10, @@ -49,7 +59,10 @@ const styles = StyleSheet.create({ alignSelf: 'center', borderRadius: 2, margin: 5, - width: 70, - height: 70, + width: 90, + height: 90, + }, + text: { + textAlign: 'center', }, }); diff --git a/src/models/BadgeForm.model.ts b/src/models/BadgeForm.model.ts index 4326808..ce16833 100644 --- a/src/models/BadgeForm.model.ts +++ b/src/models/BadgeForm.model.ts @@ -1,3 +1,5 @@ +import {type Animations} from '@/utils/animations'; + export interface BadgeConfigFormData { text: string; effects: { @@ -5,5 +7,6 @@ export interface BadgeConfigFormData { marquee: boolean; invertLed: boolean; }; + animation: Animations; speed: number; } diff --git a/src/models/SelectionType.model.ts b/src/models/SelectionType.model.ts new file mode 100644 index 0000000..fd3450b --- /dev/null +++ b/src/models/SelectionType.model.ts @@ -0,0 +1,8 @@ +import {type ImageSourcePropType} from 'react-native'; + +export interface SelectionType { + imagePath: ImageSourcePropType; + title: string; + name?: string; + code?: number; +} diff --git a/src/utils/animations.ts b/src/utils/animations.ts new file mode 100644 index 0000000..4ff747c --- /dev/null +++ b/src/utils/animations.ts @@ -0,0 +1,64 @@ +import {type SelectionType} from '@/models/SelectionType.model'; + +import animationImageSrc from '../assets/ic_anim_animation.gif'; +import downImageSrc from '../assets/ic_anim_down.gif'; +import fixedImageSrc from '../assets/ic_anim_fixed.gif'; +import laserImageSrc from '../assets/ic_anim_laser.gif'; +import leftImageSrc from '../assets/ic_anim_left.gif'; +import pictureImageSrc from '../assets/ic_anim_picture.gif'; +import rightImageSrc from '../assets/ic_anim_right.gif'; +import upImageSrc from '../assets/ic_anim_up.gif'; + +export enum Animations { + LEFT = 1, + UP = 2, + DOWN = 3, + FIXED = 4, + PICTURE = 5, + LASER = 6, + ANIMATION = 7, + RIGHT = 0, +} + +export const animations = [ + { + imagePath: rightImageSrc, + title: 'Left', + code: Animations.LEFT, + }, + { + imagePath: upImageSrc, + title: 'Up', + code: Animations.UP, + }, + { + imagePath: downImageSrc, + title: 'Down', + code: Animations.DOWN, + }, + { + imagePath: fixedImageSrc, + title: 'Fixed', + code: Animations.FIXED, + }, + { + imagePath: pictureImageSrc, + title: 'Picture', + code: Animations.PICTURE, + }, + { + imagePath: animationImageSrc, + title: 'Animation', + code: Animations.ANIMATION, + }, + { + imagePath: laserImageSrc, + title: 'Laser', + code: Animations.LASER, + }, + { + imagePath: leftImageSrc, + title: 'Right', + code: Animations.RIGHT, + }, +] as const satisfies SelectionType[]; diff --git a/src/utils/payload.ts b/src/utils/payload.ts index 708737e..f91b12e 100644 --- a/src/utils/payload.ts +++ b/src/utils/payload.ts @@ -52,13 +52,15 @@ export function getPackets(data: BadgeConfigFormData): string[] { } function buildDataHexString(data: BadgeConfigFormData): string { - const {text, effects, speed} = data; + const {text, effects, speed, animation} = data; + const payload = getLetterBitmaps(text).join(''); const size = getSize(text); const timestamp = getTimestamp(); const marquee = getMarqueeValue(effects.marquee); const flash = getFlashValue(effects.flash); - const modes = `${speed}0` + '00' + '00' + '00' + '00' + '00' + '00' + '00'; + const modes = `${speed}${animation}` + '00' + '00' + '00' + '00' + '00' + '00' + '00'; + return ( HEADER + flash + marquee + modes + size + PADDING1 + timestamp + PADDING2 + SEPARATOR + payload );