Commit 27df64e2 authored by Milan DiGiuseppe's avatar Milan DiGiuseppe

flairs!

parent f6cab58f
import React from 'react';
import {
View, StyleSheet, ViewStyle, TouchableOpacity, Text,
} from 'react-native';
import { Icon } from 'react-native-elements';
import theme from '../theme';
import GeoText from './GeoText';
export const FLAIR_TYPES = [
'artsy',
'basic',
'creative',
'drawing',
'epic',
'funny',
'gamer',
'hacker',
'italy',
'jackbox',
'karate',
'love',
'mdg',
] as const;
type Flair = typeof FLAIR_TYPES[number];
type ColorMap = {
[key in Flair]: string;
}
const COLOR_MAP: ColorMap = {
artsy: '#91F9E5',
basic: '#76F7BF',
creative: '#5FDD9D',
drawing: '#EF6461',
epic: '#2D82B7',
funny: '#DEA54B',
gamer: '#F2CD5D',
hacker: '#D741A7',
italy: '#9EADC8',
jackbox: '#E4B363',
karate: '#FFC759',
love: '#FF7B9C',
mdg: '#AD2831',
} as const;
const styles = StyleSheet.create({
flair: {
flexDirection: 'row',
alignItems: 'center',
alignSelf: 'flex-start', // hack (?) to prevent full-width
paddingHorizontal: theme.spacing.medium,
},
small: {
height: 30,
borderRadius: 30,
},
large: {
height: 40,
borderRadius: 40,
},
addFlairButton: {
backgroundColor: theme.colors.white,
borderColor: theme.colors.mediumgray,
borderWidth: 2,
borderStyle: 'solid',
},
});
interface AddFlairButtonProps {
onAdd: () => void;
style?: ViewStyle;
}
export const AddFlairButton: React.FC<AddFlairButtonProps> = ({ onAdd, style }) => (
<TouchableOpacity onPress={onAdd} style={style}>
<View style={[styles.flair, styles.addFlairButton, styles.small]}>
<GeoText text={'Add flair'} color={'mediumgray'} style={{ fontWeight: '400' }} />
</View>
</TouchableOpacity>
);
interface Props {
flair: Flair;
onRemove?: () => void;
size?: 'small' | 'large';
style?: ViewStyle;
}
const Flair: React.FC<Props> = ({
flair,
onRemove,
size = 'small',
style,
}) => (
<View style={[style, styles.flair, styles[size], { backgroundColor: COLOR_MAP[flair] }]}>
<Text style={{ color: 'white', fontWeight: '400', fontSize: size === 'small' ? 16 : 24 }}>
{flair}
</Text>
{ onRemove && (
<Icon
color={theme.colors.white}
name={'times'}
type={'font-awesome-5'}
size={12}
style={{ padding: theme.spacing.small, paddingRight: 0 }}
onPress={onRemove}
/>
)}
</View>
);
export default Flair;
......@@ -6,13 +6,14 @@ import SavedMessagesScreen from '../screens/SavedMessagesScreen';
import DiscoverScreen from '../screens/DiscoverScreen';
import SettingsScreen from '../screens/SettingsScreen';
import DevScreen from '../screens/DevScreen';
import FlairScreen from '../screens/FlairScreen';
const Stack = createStackNavigator();
const AppNavigator = () => (
<Stack.Navigator
mode={'modal'}
screenOptions={({ route, navigation }) => ({
headerShown: ['Settings'].includes(route.name),
headerShown: ['Settings', 'FlairScreen'].includes(route.name),
headerLeft: () => (
<BackButton
isX
......@@ -43,6 +44,11 @@ const AppNavigator = () => (
name={'CameraModal'}
component={CameraModal}
/>
<Stack.Screen
name={'FlairScreen'}
component={FlairScreen}
options={{ title: 'Flair' }}
/>
<Stack.Screen
name={'DevScreen'}
component={DevScreen}
......
import React, { useCallback, useMemo, useState } from 'react';
import { View, StyleSheet } from 'react-native';
import { useNavigation } from '@react-navigation/native';
import { TouchableWithoutFeedback } from 'react-native-gesture-handler';
import { useDispatch } from 'react-redux';
import theme from '../theme';
import Flair, { FLAIR_TYPES } from '../components/Flair';
import GeoButton from '../components/GeoButton';
import { setPendingCacheFlair } from '../store/pendingNewCache/PendingCacheSlice';
import ScreenContainer from './create/ScreenContainer';
const styles = StyleSheet.create({
flairsContainer: {
flex: 1,
justifyContent: 'center',
},
row: {
flexDirection: 'row',
marginVertical: theme.spacing.small,
justifyContent: 'center',
},
});
const FlairScreen = () => {
const navigation = useNavigation();
const dispatch = useDispatch();
const [flair, setFlair] = useState<Flair | undefined>();
const onSubmit = useCallback(() => {
dispatch(setPendingCacheFlair(flair));
navigation.goBack();
}, [navigation, flair]);
const rows = useMemo(() => {
const result = [];
const flairs = FLAIR_TYPES.slice();
while (flairs.length > 0) {
const count: number = (result.length % 2 === 0) ? 3 : 2;
result.push(flairs.splice(0, count));
}
return result;
}, [FLAIR_TYPES]);
return (
<ScreenContainer>
<View style={styles.flairsContainer}>
{rows.map((row) => (
<View key={row[0]} style={styles.row}>
{row.map((f) => (
<TouchableWithoutFeedback
key={f}
onPress={() => setFlair(f)}
style={{ marginHorizontal: theme.spacing.small }}
>
<Flair
flair={f}
size={'large'}
style={{
borderColor: f === flair ? theme.colors.black : 'transparent',
borderWidth: 2,
borderStyle: 'solid',
}}
/>
</TouchableWithoutFeedback>
))}
</View>
))}
</View>
<View style={{ marginHorizontal: theme.spacing.screenPadding }}>
<GeoButton
title={'Choose'}
onPress={onSubmit}
disabled={!flair}
style={{ marginVertical: theme.spacing.medium }}
/>
</View>
</ScreenContainer>
);
};
export default FlairScreen;
......@@ -3,20 +3,22 @@ import React, {
} from 'react';
import { useDispatch, useSelector } from 'react-redux';
import {
StyleSheet, TextInput, View, Image, LayoutChangeEvent,
StyleSheet, TextInput, View, Image, LayoutChangeEvent, Text,
} from 'react-native';
import { useNavigation } from '@react-navigation/native';
import * as ImagePicker from 'expo-image-picker';
import Constants from 'expo-constants';
import { ScrollView } from 'react-native-gesture-handler';
import { ImageInfo } from 'expo-image-picker/build/ImagePicker.types';
import { Avatar } from 'react-native-elements';
import theme from '../../theme';
import GeoText from '../../components/GeoText';
import { setPendingCacheContent, setPendingCacheImage } from '../../store/pendingNewCache/PendingCacheSlice';
import { setPendingCacheContent, setPendingCacheImage, setPendingCacheFlair } from '../../store/pendingNewCache/PendingCacheSlice';
import GeoButton from '../../components/GeoButton';
import ScreenContainer from './ScreenContainer';
import IconButton from '../../components/IconButton';
import { getPendingCacheImage } from '../../store/pendingNewCache/PendingCacheSelectors';
import { getPendingCacheImage, getPendingCacheFlair } from '../../store/pendingNewCache/PendingCacheSelectors';
import Flair, { AddFlairButton } from '../../components/Flair';
const CHAR_MAX = 100;
......@@ -25,6 +27,19 @@ const styles = StyleSheet.create({
flex: 1,
paddingHorizontal: theme.spacing.screenPadding,
},
profileRow: {
flexDirection: 'row',
alignItems: 'center',
marginTop: theme.spacing.medium,
},
textArea: {
marginTop: theme.spacing.small,
fontSize: 20,
backgroundColor: theme.colors.lightgray,
borderRadius: theme.spacing.small,
padding: theme.spacing.medium,
paddingTop: theme.spacing.medium,
},
textAreaFooter: {
flex: 1,
justifyContent: 'space-between',
......@@ -77,6 +92,10 @@ const CacheMessageScreen: React.FC = () => {
navigate('CacheDuration');
}, [dispatch, navigate, text]);
const flair = useSelector(getPendingCacheFlair);
const onAddFlair = useCallback(() => navigate('FlairScreen'), [navigate]);
const onRemoveFlair = useCallback(() => dispatch(setPendingCacheFlair(undefined)), [dispatch]);
return (
<ScreenContainer>
<View style={styles.container}>
......@@ -85,24 +104,32 @@ const CacheMessageScreen: React.FC = () => {
showsVerticalScrollIndicator={false}
onLayout={onScrollViewLayout}
>
<GeoText
variant={'formHeader'}
text={'milan.digiuseppe'}
style={{ marginTop: theme.spacing.medium }}
/>
<View style={styles.profileRow}>
<Avatar
rounded
title={'MD'}
containerStyle={{ backgroundColor: theme.colors.mediumgray }}
size={'small'}
/>
<GeoText
variant={'formHeader'}
text={'milan.digiuseppe'}
style={{ marginLeft: theme.spacing.medium }}
/>
</View>
<View style={{ marginTop: theme.spacing.medium }}>
{flair ? (
<Flair flair={flair} onRemove={onRemoveFlair} />
) : (
<AddFlairButton onAdd={onAddFlair} />
)}
</View>
<TextInput
value={text}
onChangeText={setText}
multiline
maxLength={CHAR_MAX}
style={{
marginTop: theme.spacing.medium,
fontSize: 20,
backgroundColor: theme.colors.lightgray,
borderRadius: theme.spacing.small,
padding: theme.spacing.medium,
paddingTop: theme.spacing.medium,
}}
style={styles.textArea}
placeholder={'My cache...'}
/>
<View style={styles.textAreaFooter}>
......
......@@ -3,3 +3,4 @@ import { RootState } from '../RootReducer';
export const getPendingCacheContent = (state: RootState) => state.pendingCache.content;
export const getPendingCacheDuration = (state: RootState) => state.pendingCache.duration;
export const getPendingCacheImage = (state: RootState) => state.pendingCache.image;
export const getPendingCacheFlair = (state: RootState) => state.pendingCache.flair;
import { createSlice } from '@reduxjs/toolkit';
import Flair from '../../components/Flair';
export const DURATION_OPTIONS = [1, 4, 12, 24] as const;
type DurationOption = typeof DURATION_OPTIONS[number]
......@@ -13,6 +14,7 @@ export interface PendingNewCacheState {
content: string;
duration: DurationOption;
image?: ImageInfo;
flair?: Flair;
}
const pendingCacheSlice = createSlice({
......@@ -31,6 +33,9 @@ const pendingCacheSlice = createSlice({
setImage(state, action) {
state.image = action.payload;
},
setFlair(state, action) {
state.flair = action.payload;
},
},
});
......@@ -39,4 +44,5 @@ export const {
setContent: setPendingCacheContent,
setDuration: setPendingCacheDuration,
setImage: setPendingCacheImage,
setFlair: setPendingCacheFlair,
} = pendingCacheSlice.actions;
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment