Fix layout bugs
Signed-off-by: Nikolaos Karaolidis <nick@karaolidis.com>
This commit is contained in:
@@ -1,17 +1,28 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { Keyboard } from 'react-native';
|
||||
import { Keyboard, StyleSheet } from 'react-native';
|
||||
import { FAB } from 'react-native-paper';
|
||||
import { ParamListBase, useNavigation } from '@react-navigation/native';
|
||||
import { NativeStackNavigationProp } from '@react-navigation/native-stack';
|
||||
import { pick } from 'react-native-document-picker';
|
||||
import { useDimensions } from '../contexts';
|
||||
import { ORIENTATION, useDimensions } from '../contexts';
|
||||
import { ROUTE } from '../types';
|
||||
import { allowedMimeTypes, noOp } from '../utilities';
|
||||
|
||||
const floatingActionButtonStyles = StyleSheet.create({
|
||||
fab: {
|
||||
paddingBottom: 90,
|
||||
paddingRight: 10,
|
||||
},
|
||||
fabLandscape: {
|
||||
paddingBottom: 40,
|
||||
paddingRight: 12,
|
||||
},
|
||||
});
|
||||
|
||||
const FloatingActionButton = ({ visible = true }: { visible?: boolean }) => {
|
||||
const { navigate } =
|
||||
useNavigation<NativeStackNavigationProp<ParamListBase>>();
|
||||
const { responsive } = useDimensions();
|
||||
const { orientation } = useDimensions();
|
||||
|
||||
const [state, setState] = useState(false);
|
||||
const [keyboardOpen, setKeyboardOpen] = useState(false);
|
||||
@@ -50,18 +61,6 @@ const FloatingActionButton = ({ visible = true }: { visible?: boolean }) => {
|
||||
throw new Error('Not yet implemented');
|
||||
},
|
||||
},
|
||||
{
|
||||
icon: 'image-album',
|
||||
label: 'Album',
|
||||
onPress: async () => {
|
||||
const res = await pick({
|
||||
allowMultiSelection: true,
|
||||
type: allowedMimeTypes,
|
||||
}).catch(noOp);
|
||||
if (!res) return;
|
||||
navigate(ROUTE.ADD_MEME, { uri: res });
|
||||
},
|
||||
},
|
||||
]}
|
||||
onStateChange={({ open }) => setState(open)}
|
||||
onPress={async () => {
|
||||
@@ -70,10 +69,11 @@ const FloatingActionButton = ({ visible = true }: { visible?: boolean }) => {
|
||||
if (!res) return;
|
||||
navigate(ROUTE.ADD_MEME, { uri: res });
|
||||
}}
|
||||
style={{
|
||||
paddingBottom: responsive.verticalScale(75),
|
||||
paddingRight: responsive.horizontalScale(10),
|
||||
}}
|
||||
style={
|
||||
orientation === ORIENTATION.PORTRAIT
|
||||
? floatingActionButtonStyles.fab
|
||||
: floatingActionButtonStyles.fabLandscape
|
||||
}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
@@ -1,6 +1,6 @@
|
||||
import React, { useEffect, useRef } from 'react';
|
||||
import { Animated, StyleSheet } from 'react-native';
|
||||
import { useDimensions } from '../contexts';
|
||||
import { ORIENTATION, useDimensions } from '../contexts';
|
||||
import styles from '../styles';
|
||||
import { useTheme } from 'react-native-paper';
|
||||
|
||||
@@ -38,8 +38,9 @@ const HideableHeader = ({
|
||||
<Animated.View
|
||||
style={[
|
||||
hideableHeaderStyles.headerView,
|
||||
orientation == 'portrait' && styles.paddingTop,
|
||||
orientation == 'landscape' && styles.smallPaddingTop,
|
||||
orientation === ORIENTATION.PORTRAIT
|
||||
? styles.paddingTop
|
||||
: styles.smallPaddingTop,
|
||||
styles.paddingHorizontal,
|
||||
{
|
||||
transform: [
|
||||
|
@@ -1,5 +1,10 @@
|
||||
export { MemeEditor } from './memes';
|
||||
export { TagChip, TagEditor, TagPreview } from './tags';
|
||||
export {
|
||||
MemeEditor,
|
||||
MemesHeader,
|
||||
MemeTagSearchModal,
|
||||
MemeTagSelector,
|
||||
} from './memes';
|
||||
export { TagChip, TagEditor, TagPreview, TagRow, TagsHeader } from './tags';
|
||||
export { default as FloatingActionButton } from './floatingActionButton';
|
||||
export { default as HideableBottomNavigationBar } from './hideableBottomNavigationBar';
|
||||
export { default as HideableHeader } from './hideableHeader';
|
||||
|
@@ -1,3 +1,4 @@
|
||||
export { default as MemeEditor } from './memeEditor';
|
||||
export { default as MemesHeader } from './memesHeader';
|
||||
export { default as MemeTagSearchModal } from './memeTagSearchModal';
|
||||
export { default as MemeTagSelector } from './memeTagSelector';
|
||||
|
@@ -11,6 +11,19 @@ import {
|
||||
validateMemeTitle,
|
||||
} from '../../utilities';
|
||||
|
||||
const memeEditorStyles = {
|
||||
image: {
|
||||
marginBottom: 15,
|
||||
borderRadius: 5,
|
||||
},
|
||||
memeTagSelector: {
|
||||
marginBottom: 10,
|
||||
},
|
||||
description: {
|
||||
marginBottom: 10,
|
||||
},
|
||||
};
|
||||
|
||||
const MemeEditor = ({
|
||||
imageUri,
|
||||
memeTitle,
|
||||
@@ -20,7 +33,7 @@ const MemeEditor = ({
|
||||
memeTags,
|
||||
setMemeTags,
|
||||
}: {
|
||||
imageUri: string[];
|
||||
imageUri: string;
|
||||
memeTitle: StringValidationResult;
|
||||
setMemeTitle: (name: StringValidationResult) => void;
|
||||
memeDescription: StringValidationResult;
|
||||
@@ -28,14 +41,14 @@ const MemeEditor = ({
|
||||
memeTags: Map<string, Tag>;
|
||||
setMemeTags: (tags: Map<string, Tag>) => void;
|
||||
}) => {
|
||||
const { dimensions, fixed, responsive } = useDimensions();
|
||||
const { dimensions } = useDimensions();
|
||||
|
||||
const [imageWidth, setImageWidth] = useState<number>();
|
||||
const [imageHeight, setImageHeight] = useState<number>();
|
||||
|
||||
useEffect(() => {
|
||||
Image.getSize(imageUri[0], (width, height) => {
|
||||
const paddedWidth = dimensions.width - dimensions.width * 0.08;
|
||||
Image.getSize(imageUri, (width, height) => {
|
||||
const paddedWidth = dimensions.width * 0.92;
|
||||
setImageWidth(paddedWidth);
|
||||
setImageHeight((paddedWidth / width) * height);
|
||||
});
|
||||
@@ -57,33 +70,31 @@ const MemeEditor = ({
|
||||
{memeTitle.error}
|
||||
</HelperText>
|
||||
<Image
|
||||
source={{ uri: imageUri[0] }}
|
||||
style={{
|
||||
width: imageWidth,
|
||||
height: imageHeight,
|
||||
marginBottom: fixed.verticalScale(10),
|
||||
borderRadius: fixed.verticalScale(5),
|
||||
}}
|
||||
source={{ uri: imageUri }}
|
||||
style={[
|
||||
{
|
||||
width: imageWidth,
|
||||
height: imageHeight,
|
||||
},
|
||||
memeEditorStyles.image,
|
||||
]}
|
||||
/>
|
||||
<MemeTagSelector
|
||||
memeTags={memeTags}
|
||||
setMemeTags={setMemeTags}
|
||||
style={{
|
||||
marginBottom: responsive.verticalScale(10),
|
||||
}}
|
||||
style={memeEditorStyles.memeTagSelector}
|
||||
/>
|
||||
<TextInput
|
||||
mode="outlined"
|
||||
label="Description"
|
||||
multiline
|
||||
numberOfLines={6}
|
||||
style={{
|
||||
marginBottom: responsive.verticalScale(15),
|
||||
}}
|
||||
value={memeDescription.raw}
|
||||
style={memeEditorStyles.description}
|
||||
onChangeText={description =>
|
||||
setMemeDescription(validateMemeDescription(description))
|
||||
}
|
||||
error={!memeDescription.valid}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
|
@@ -5,7 +5,6 @@ import { useQuery, useRealm } from '@realm/react';
|
||||
import { TAG_SORT, tagSortQuery } from '../../types';
|
||||
import { Chip, Modal, Portal, Searchbar, useTheme } from 'react-native-paper';
|
||||
import { StyleSheet } from 'react-native';
|
||||
import { useDimensions } from '../../contexts';
|
||||
import styles from '../../styles';
|
||||
import { FlashList } from '@shopify/flash-list';
|
||||
import { validateTagName } from '../../utilities';
|
||||
@@ -14,6 +13,15 @@ const memeTagSearchModalStyles = StyleSheet.create({
|
||||
modal: {
|
||||
position: 'absolute',
|
||||
bottom: 0,
|
||||
padding: 10,
|
||||
borderTopLeftRadius: 20,
|
||||
borderTopRightRadius: 20,
|
||||
},
|
||||
searchbar: {
|
||||
marginBottom: 12,
|
||||
},
|
||||
tagChip: {
|
||||
marginRight: 8,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -29,7 +37,6 @@ const MemeTagSearchModal = ({
|
||||
setMemeTags: (tags: Map<string, Tag>) => void;
|
||||
}) => {
|
||||
const { colors } = useTheme();
|
||||
const { fixed, responsive } = useDimensions();
|
||||
const realm = useRealm();
|
||||
|
||||
const flashListRef = useRef<FlashList<Tag>>(null);
|
||||
@@ -90,9 +97,6 @@ const MemeTagSearchModal = ({
|
||||
visible={visible}
|
||||
contentContainerStyle={[
|
||||
{
|
||||
padding: fixed.horizontalScale(10),
|
||||
borderTopLeftRadius: fixed.verticalScale(20),
|
||||
borderTopRightRadius: fixed.verticalScale(20),
|
||||
backgroundColor: colors.surface,
|
||||
},
|
||||
styles.fullWidth,
|
||||
@@ -103,9 +107,7 @@ const MemeTagSearchModal = ({
|
||||
placeholder="Search or Create Tags"
|
||||
onChangeText={handleSearch}
|
||||
value={search}
|
||||
style={{
|
||||
marginBottom: responsive.verticalScale(10),
|
||||
}}
|
||||
style={memeTagSearchModalStyles.searchbar}
|
||||
autoFocus
|
||||
/>
|
||||
<FlashList
|
||||
@@ -119,9 +121,7 @@ const MemeTagSearchModal = ({
|
||||
renderItem={({ item: tag }) => (
|
||||
<TagChip
|
||||
tag={tag}
|
||||
style={{
|
||||
marginRight: fixed.horizontalScale(5),
|
||||
}}
|
||||
style={memeTagSearchModalStyles.tagChip}
|
||||
onPress={() => handleTagPress(tag)}
|
||||
active={memeTags.has(tag.id.toHexString())}
|
||||
/>
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import React, { ComponentProps, useState } from 'react';
|
||||
import { View } from 'react-native';
|
||||
import { StyleSheet, View } from 'react-native';
|
||||
import { Chip } from 'react-native-paper';
|
||||
import { TagChip } from '../tags';
|
||||
import { Tag } from '../../database';
|
||||
@@ -7,6 +7,12 @@ import { useDimensions } from '../../contexts';
|
||||
import { MemeTagSearchModal } from '.';
|
||||
import { FlashList } from '@shopify/flash-list';
|
||||
|
||||
const memeTagSelectorStyles = StyleSheet.create({
|
||||
tagChip: {
|
||||
marginRight: 8,
|
||||
},
|
||||
});
|
||||
|
||||
const MemeTagSelector = ({
|
||||
memeTags,
|
||||
setMemeTags,
|
||||
@@ -15,8 +21,9 @@ const MemeTagSelector = ({
|
||||
memeTags: Map<string, Tag>;
|
||||
setMemeTags: (tags: Map<string, Tag>) => void;
|
||||
} & ComponentProps<typeof View>) => {
|
||||
const { fixed, dimensions } = useDimensions();
|
||||
const { dimensions } = useDimensions();
|
||||
|
||||
const [flashListMargin, setFlashListMargin] = useState(0);
|
||||
const [tagSearchModalVisible, setTagSearchModalVisible] = useState(false);
|
||||
|
||||
const handleTagPress = (tag: Tag) => {
|
||||
@@ -38,9 +45,7 @@ const MemeTagSelector = ({
|
||||
<TagChip
|
||||
tag={tag}
|
||||
onPress={() => handleTagPress(tag)}
|
||||
style={{
|
||||
marginRight: fixed.horizontalScale(5),
|
||||
}}
|
||||
style={memeTagSelectorStyles.tagChip}
|
||||
/>
|
||||
)}
|
||||
ListFooterComponent={() => (
|
||||
@@ -48,8 +53,13 @@ const MemeTagSelector = ({
|
||||
icon="plus"
|
||||
mode="outlined"
|
||||
onPress={() => setTagSearchModalVisible(true)}
|
||||
onLayout={event =>
|
||||
setFlashListMargin(
|
||||
dimensions.width * 0.92 - event.nativeEvent.layout.width,
|
||||
)
|
||||
}
|
||||
style={{
|
||||
marginRight: dimensions.width * 0.92 - 105.8,
|
||||
marginRight: flashListMargin,
|
||||
}}>
|
||||
Add Tag
|
||||
</Chip>
|
||||
|
167
src/components/memes/memesHeader.tsx
Normal file
167
src/components/memes/memesHeader.tsx
Normal file
@@ -0,0 +1,167 @@
|
||||
import React, { ComponentProps, useState } from 'react';
|
||||
import { StyleSheet, View } from 'react-native';
|
||||
import {
|
||||
Button,
|
||||
Divider,
|
||||
IconButton,
|
||||
Menu,
|
||||
Searchbar,
|
||||
useTheme,
|
||||
} from 'react-native-paper';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import { MEME_TYPE, memeTypePlural } from '../../database';
|
||||
import {
|
||||
RootState,
|
||||
cycleMemesView,
|
||||
setMemesFilter,
|
||||
setMemesSort,
|
||||
setMemesSortDirection,
|
||||
toggleMemesFavoritesOnly,
|
||||
toggleMemesSortDirection,
|
||||
} from '../../state';
|
||||
import styles from '../../styles';
|
||||
import { MEME_SORT, SORT_DIRECTION } from '../../types';
|
||||
import { getSortIcon, getViewIcon } from '../../utilities';
|
||||
|
||||
const memesHeaderStyles = StyleSheet.create({
|
||||
buttonView: {
|
||||
height: 50,
|
||||
},
|
||||
});
|
||||
|
||||
const MemesHeader = ({
|
||||
search,
|
||||
setSearch,
|
||||
...props
|
||||
}: {
|
||||
search: string;
|
||||
setSearch: (search: string) => void;
|
||||
} & ComponentProps<typeof View>) => {
|
||||
const { colors } = useTheme();
|
||||
const sort = useSelector((state: RootState) => state.memes.sort);
|
||||
const sortDirection = useSelector(
|
||||
(state: RootState) => state.memes.sortDirection,
|
||||
);
|
||||
const view = useSelector((state: RootState) => state.memes.view);
|
||||
const favoritesOnly = useSelector(
|
||||
(state: RootState) => state.memes.favoritesOnly,
|
||||
);
|
||||
const filter = useSelector((state: RootState) => state.memes.filter);
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const [sortMenuVisible, setSortMenuVisible] = useState(false);
|
||||
const [filterMenuVisible, setFilterMenuVisible] = useState(false);
|
||||
|
||||
const handleSortModeChange = (newSort: MEME_SORT) => {
|
||||
if (newSort === sort) {
|
||||
dispatch(toggleMemesSortDirection());
|
||||
} else {
|
||||
dispatch(setMemesSort(newSort));
|
||||
if (newSort === MEME_SORT.TITLE) {
|
||||
dispatch(setMemesSortDirection(SORT_DIRECTION.ASCENDING));
|
||||
} else {
|
||||
dispatch(setMemesSortDirection(SORT_DIRECTION.DESCENDING));
|
||||
}
|
||||
}
|
||||
setSortMenuVisible(false);
|
||||
};
|
||||
|
||||
const handleFilterChange = (newFilter: MEME_TYPE | undefined) => {
|
||||
dispatch(setMemesFilter(newFilter));
|
||||
setFilterMenuVisible(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<View {...props}>
|
||||
<Searchbar
|
||||
placeholder="Search Memes"
|
||||
value={search}
|
||||
onChangeText={setSearch}
|
||||
/>
|
||||
<View
|
||||
style={[
|
||||
styles.flexRowSpaceBetween,
|
||||
styles.alignCenter,
|
||||
memesHeaderStyles.buttonView,
|
||||
]}>
|
||||
<View style={[styles.flexRow, styles.alignCenter]}>
|
||||
<Menu
|
||||
visible={sortMenuVisible}
|
||||
onDismiss={() => setSortMenuVisible(false)}
|
||||
anchor={
|
||||
<Button
|
||||
onPress={() => setSortMenuVisible(true)}
|
||||
icon={getSortIcon(sort, sortDirection)}
|
||||
contentStyle={styles.flexRowReverse}
|
||||
compact>
|
||||
Sort By: {sort}
|
||||
</Button>
|
||||
}>
|
||||
{Object.keys(MEME_SORT).map(key => {
|
||||
return (
|
||||
<Menu.Item
|
||||
key={key}
|
||||
onPress={() =>
|
||||
handleSortModeChange(
|
||||
MEME_SORT[key as keyof typeof MEME_SORT],
|
||||
)
|
||||
}
|
||||
title={MEME_SORT[key as keyof typeof MEME_SORT]}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</Menu>
|
||||
</View>
|
||||
<View style={[styles.flexRow, styles.alignCenter]}>
|
||||
<IconButton
|
||||
icon={getViewIcon(view)}
|
||||
iconColor={colors.primary}
|
||||
size={16}
|
||||
animated
|
||||
onPress={() => dispatch(cycleMemesView())}
|
||||
/>
|
||||
<IconButton
|
||||
icon={favoritesOnly ? 'heart' : 'heart-outline'}
|
||||
iconColor={colors.primary}
|
||||
size={16}
|
||||
animated
|
||||
onPress={() => dispatch(toggleMemesFavoritesOnly())}
|
||||
/>
|
||||
<Menu
|
||||
visible={filterMenuVisible}
|
||||
onDismiss={() => setFilterMenuVisible(false)}
|
||||
anchor={
|
||||
<IconButton
|
||||
onPress={() => setFilterMenuVisible(true)}
|
||||
icon={filter ? 'filter' : 'filter-outline'}
|
||||
iconColor={colors.primary}
|
||||
size={16}
|
||||
/>
|
||||
}>
|
||||
<Menu.Item
|
||||
// eslint-disable-next-line unicorn/no-useless-undefined
|
||||
onPress={() => handleFilterChange(undefined)}
|
||||
title="All"
|
||||
/>
|
||||
{Object.keys(MEME_TYPE).map(key => {
|
||||
return (
|
||||
<Menu.Item
|
||||
key={key}
|
||||
onPress={() =>
|
||||
handleFilterChange(MEME_TYPE[key as keyof typeof MEME_TYPE])
|
||||
}
|
||||
title={
|
||||
memeTypePlural[MEME_TYPE[key as keyof typeof MEME_TYPE]]
|
||||
}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</Menu>
|
||||
</View>
|
||||
</View>
|
||||
<Divider />
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
export default MemesHeader;
|
@@ -1,3 +1,5 @@
|
||||
export { default as TagChip } from './tagChip';
|
||||
export { default as TagEditor } from './tagEditor';
|
||||
export { default as TagPreview } from './tagPreview';
|
||||
export { default as TagRow } from './tagRow';
|
||||
export { default as TagsHeader } from './tagsHeader';
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import React, { useMemo } from 'react';
|
||||
import React, { ComponentProps, useMemo } from 'react';
|
||||
import { getContrastColor } from '../../utilities';
|
||||
import { Chip, useTheme } from 'react-native-paper';
|
||||
import { Tag } from '../../database';
|
||||
@@ -20,7 +20,7 @@ const TagChip = ({
|
||||
tag: Tag;
|
||||
active?: boolean;
|
||||
onPress?: () => void;
|
||||
} & Omit<React.ComponentProps<typeof Chip>, 'children'>) => {
|
||||
} & Omit<ComponentProps<typeof Chip>, 'children'>) => {
|
||||
const theme = useTheme();
|
||||
|
||||
const chipTheme = useMemo(() => {
|
||||
|
@@ -35,7 +35,6 @@ const TagEditor = ({
|
||||
value={tagName.raw}
|
||||
onChangeText={name => setTagName(validateTagName(name))}
|
||||
error={!tagName.valid}
|
||||
autoCapitalize="none"
|
||||
selectTextOnFocus
|
||||
/>
|
||||
<HelperText type="error" visible={!tagName.valid}>
|
||||
|
37
src/components/tags/tagRow.tsx
Normal file
37
src/components/tags/tagRow.tsx
Normal file
@@ -0,0 +1,37 @@
|
||||
import React from 'react';
|
||||
import { StyleSheet, View } from 'react-native';
|
||||
import { TouchableRipple, Text } from 'react-native-paper';
|
||||
import { useNavigation, NavigationProp } from '@react-navigation/native';
|
||||
import { Tag } from '../../database';
|
||||
import { ROUTE, RootStackParamList } from '../../types';
|
||||
import { TagChip } from '.';
|
||||
|
||||
const tagRowStyles = StyleSheet.create({
|
||||
view: {
|
||||
justifyContent: 'space-between',
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
paddingVertical: 10,
|
||||
paddingHorizontal: 15,
|
||||
},
|
||||
tagChip: {
|
||||
flexShrink: 1,
|
||||
maxWidth: '80%',
|
||||
},
|
||||
});
|
||||
|
||||
const TagRow = ({ tag }: { tag: Tag }) => {
|
||||
const { navigate } = useNavigation<NavigationProp<RootStackParamList>>();
|
||||
|
||||
return (
|
||||
<TouchableRipple
|
||||
onPress={() => navigate(ROUTE.EDIT_TAG, { id: tag.id.toHexString() })}>
|
||||
<View style={tagRowStyles.view}>
|
||||
<TagChip tag={tag} style={tagRowStyles.tagChip} />
|
||||
<Text>{tag.memesLength}</Text>
|
||||
</View>
|
||||
</TouchableRipple>
|
||||
);
|
||||
};
|
||||
|
||||
export default TagRow;
|
96
src/components/tags/tagsHeader.tsx
Normal file
96
src/components/tags/tagsHeader.tsx
Normal file
@@ -0,0 +1,96 @@
|
||||
import React, { ComponentProps, useState } from 'react';
|
||||
import { StyleSheet, View } from 'react-native';
|
||||
import { Button, Divider, Menu, Searchbar } from 'react-native-paper';
|
||||
import styles from '../../styles';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import {
|
||||
RootState,
|
||||
setTagsSort,
|
||||
setTagsSortDirection,
|
||||
toggleTagsSortDirection,
|
||||
} from '../../state';
|
||||
import { SORT_DIRECTION, TAG_SORT } from '../../types';
|
||||
import { getSortIcon } from '../../utilities';
|
||||
|
||||
const tagsHeaderStyles = StyleSheet.create({
|
||||
buttonView: {
|
||||
height: 50,
|
||||
},
|
||||
});
|
||||
|
||||
const TagsHeader = ({
|
||||
search,
|
||||
setSearch,
|
||||
...props
|
||||
}: {
|
||||
search: string;
|
||||
setSearch: (search: string) => void;
|
||||
} & ComponentProps<typeof View>) => {
|
||||
const sort = useSelector((state: RootState) => state.tags.sort);
|
||||
const sortDirection = useSelector(
|
||||
(state: RootState) => state.tags.sortDirection,
|
||||
);
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const [sortMenuVisible, setSortMenuVisible] = useState(false);
|
||||
|
||||
const handleSortModeChange = (newSort: TAG_SORT) => {
|
||||
if (newSort === sort) {
|
||||
dispatch(toggleTagsSortDirection());
|
||||
} else {
|
||||
dispatch(setTagsSort(newSort));
|
||||
if (newSort === TAG_SORT.NAME) {
|
||||
dispatch(setTagsSortDirection(SORT_DIRECTION.ASCENDING));
|
||||
} else {
|
||||
dispatch(setTagsSortDirection(SORT_DIRECTION.DESCENDING));
|
||||
}
|
||||
}
|
||||
setSortMenuVisible(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<View {...props}>
|
||||
<Searchbar
|
||||
placeholder="Search Tags"
|
||||
value={search}
|
||||
onChangeText={(value: string) => {
|
||||
setSearch(value);
|
||||
}}
|
||||
/>
|
||||
<View
|
||||
style={[
|
||||
styles.flexRow,
|
||||
styles.alignCenter,
|
||||
tagsHeaderStyles.buttonView,
|
||||
]}>
|
||||
<Menu
|
||||
visible={sortMenuVisible}
|
||||
onDismiss={() => setSortMenuVisible(false)}
|
||||
anchor={
|
||||
<Button
|
||||
onPress={() => setSortMenuVisible(true)}
|
||||
icon={getSortIcon(sort, sortDirection)}
|
||||
contentStyle={styles.flexRowReverse}
|
||||
compact>
|
||||
Sort By: {sort}
|
||||
</Button>
|
||||
}>
|
||||
{Object.keys(TAG_SORT).map(key => {
|
||||
return (
|
||||
<Menu.Item
|
||||
key={key}
|
||||
onPress={() =>
|
||||
handleSortModeChange(TAG_SORT[key as keyof typeof TAG_SORT])
|
||||
}
|
||||
title={TAG_SORT[key as keyof typeof TAG_SORT]}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</Menu>
|
||||
</View>
|
||||
<Divider />
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
export default TagsHeader;
|
Reference in New Issue
Block a user