Refactor validation
Signed-off-by: Nikolaos Karaolidis <nick@karaolidis.com>
This commit is contained in:
@@ -5,6 +5,11 @@ import { useDimensions } from '../../contexts';
|
||||
import LoadingView from '../loadingView';
|
||||
import { MemeTagSelector } from '.';
|
||||
import { Tag } from '../../database';
|
||||
import {
|
||||
StringValidationResult,
|
||||
validateMemeDescription,
|
||||
validateMemeTitle,
|
||||
} from '../../utilities';
|
||||
|
||||
const MemeEditor = ({
|
||||
imageUri,
|
||||
@@ -14,18 +19,14 @@ const MemeEditor = ({
|
||||
setMemeDescription,
|
||||
memeTags,
|
||||
setMemeTags,
|
||||
memeTitleError,
|
||||
setMemeTitleError,
|
||||
}: {
|
||||
imageUri: string[];
|
||||
memeTitle: string;
|
||||
setMemeTitle: (name: string) => void;
|
||||
memeDescription: string;
|
||||
setMemeDescription: (description: string) => void;
|
||||
memeTitle: StringValidationResult;
|
||||
setMemeTitle: (name: StringValidationResult) => void;
|
||||
memeDescription: StringValidationResult;
|
||||
setMemeDescription: (description: StringValidationResult) => void;
|
||||
memeTags: Map<string, Tag>;
|
||||
setMemeTags: (tags: Map<string, Tag>) => void;
|
||||
memeTitleError: string | undefined;
|
||||
setMemeTitleError: (error: string | undefined) => void;
|
||||
}) => {
|
||||
const { dimensions, fixed, responsive } = useDimensions();
|
||||
|
||||
@@ -40,16 +41,6 @@ const MemeEditor = ({
|
||||
});
|
||||
}, [dimensions.width, imageUri]);
|
||||
|
||||
const handleMemeTitleChange = (name: string) => {
|
||||
setMemeTitle(name);
|
||||
if (name.length === 0) {
|
||||
setMemeTitleError('Meme title cannot be empty');
|
||||
} else {
|
||||
// eslint-disable-next-line unicorn/no-useless-undefined
|
||||
setMemeTitleError(undefined);
|
||||
}
|
||||
};
|
||||
|
||||
if (!imageWidth || !imageHeight) return <LoadingView />;
|
||||
|
||||
return (
|
||||
@@ -57,13 +48,13 @@ const MemeEditor = ({
|
||||
<TextInput
|
||||
mode="outlined"
|
||||
label="Title"
|
||||
value={memeTitle}
|
||||
onChangeText={handleMemeTitleChange}
|
||||
error={!!memeTitleError}
|
||||
value={memeTitle.raw}
|
||||
onChangeText={title => setMemeTitle(validateMemeTitle(title))}
|
||||
error={!memeTitle.valid}
|
||||
selectTextOnFocus
|
||||
/>
|
||||
<HelperText type="error" visible={!!memeTitleError}>
|
||||
{memeTitleError}
|
||||
<HelperText type="error" visible={!memeTitle.valid}>
|
||||
{memeTitle.error}
|
||||
</HelperText>
|
||||
<Image
|
||||
source={{ uri: imageUri[0] }}
|
||||
@@ -89,8 +80,10 @@ const MemeEditor = ({
|
||||
style={{
|
||||
marginBottom: responsive.verticalScale(15),
|
||||
}}
|
||||
value={memeDescription}
|
||||
onChangeText={setMemeDescription}
|
||||
value={memeDescription.raw}
|
||||
onChangeText={description =>
|
||||
setMemeDescription(validateMemeDescription(description))
|
||||
}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import React, { useRef, useState } from 'react';
|
||||
import React, { useEffect, useRef, useState } from 'react';
|
||||
import { TagChip } from '../tags';
|
||||
import { Tag } from '../../database';
|
||||
import { useQuery, useRealm } from '@realm/react';
|
||||
@@ -8,6 +8,7 @@ import { StyleSheet } from 'react-native';
|
||||
import { useDimensions } from '../../contexts';
|
||||
import styles from '../../styles';
|
||||
import { FlashList } from '@shopify/flash-list';
|
||||
import { validateTagName } from '../../utilities';
|
||||
|
||||
const memeTagSearchModalStyles = StyleSheet.create({
|
||||
modal: {
|
||||
@@ -34,6 +35,11 @@ const MemeTagSearchModal = ({
|
||||
const flashListRef = useRef<FlashList<Tag>>(null);
|
||||
|
||||
const [search, setSearch] = useState('');
|
||||
const [tagName, setTagName] = useState(validateTagName(search));
|
||||
|
||||
useEffect(() => {
|
||||
setTagName(validateTagName(search));
|
||||
}, [search]);
|
||||
|
||||
const handleSearch = (newSearch: string) => {
|
||||
flashListRef.current?.scrollToOffset({ offset: 0 });
|
||||
@@ -42,10 +48,20 @@ const MemeTagSearchModal = ({
|
||||
|
||||
const tags = useQuery<Tag>(
|
||||
Tag.schema.name,
|
||||
collection =>
|
||||
collection
|
||||
.filtered(`name CONTAINS[c] "${search}"`)
|
||||
.sorted(tagSortQuery(TAG_SORT.DATE_MODIFIED), true),
|
||||
collectionIn => {
|
||||
let collection = collectionIn;
|
||||
|
||||
if (search) {
|
||||
collection = collection.filtered('name CONTAINS[c] $0', search);
|
||||
}
|
||||
|
||||
collection = collection.sorted(
|
||||
tagSortQuery(TAG_SORT.DATE_MODIFIED),
|
||||
true,
|
||||
);
|
||||
|
||||
return collection;
|
||||
},
|
||||
[search],
|
||||
);
|
||||
|
||||
@@ -114,8 +130,10 @@ const MemeTagSearchModal = ({
|
||||
<Chip
|
||||
icon="plus"
|
||||
mode="outlined"
|
||||
onPress={() => handleCreateTag(search.replaceAll(/\s+/g, ''))}>
|
||||
Create Tag #{search.replaceAll(/\s+/g, '')}
|
||||
onPress={() =>
|
||||
handleCreateTag(tagName.valid ? tagName.parsed : 'newTag')
|
||||
}>
|
||||
Create Tag #{tagName.valid ? tagName.parsed : 'newTag'}
|
||||
</Chip>
|
||||
)}
|
||||
/>
|
||||
|
@@ -1,77 +1,52 @@
|
||||
import React from 'react';
|
||||
import React, { useRef } from 'react';
|
||||
import { HelperText, TextInput } from 'react-native-paper';
|
||||
import TagPreview from './tagPreview';
|
||||
import { generateRandomColor, isValidColor } from '../../utilities';
|
||||
import {
|
||||
StringValidationResult,
|
||||
generateRandomColor,
|
||||
validateColor,
|
||||
validateTagName,
|
||||
} from '../../utilities';
|
||||
|
||||
const TagEditor = ({
|
||||
tagName,
|
||||
setTagName,
|
||||
tagColor,
|
||||
setTagColor,
|
||||
validatedTagColor,
|
||||
setValidatedTagColor,
|
||||
tagNameError,
|
||||
setTagNameError,
|
||||
tagColorError,
|
||||
setTagColorError,
|
||||
}: {
|
||||
tagName: string;
|
||||
setTagName: (name: string) => void;
|
||||
tagColor: string;
|
||||
setTagColor: (color: string) => void;
|
||||
validatedTagColor: string;
|
||||
setValidatedTagColor: (color: string) => void;
|
||||
tagNameError: string | undefined;
|
||||
setTagNameError: (error: string | undefined) => void;
|
||||
tagColorError: string | undefined;
|
||||
setTagColorError: (error: string | undefined) => void;
|
||||
tagName: StringValidationResult;
|
||||
setTagName: (name: StringValidationResult) => void;
|
||||
tagColor: StringValidationResult;
|
||||
setTagColor: (color: StringValidationResult) => void;
|
||||
}) => {
|
||||
const handleTagNameChange = (name: string) => {
|
||||
setTagName(name);
|
||||
|
||||
if (name.length === 0) {
|
||||
setTagNameError('Tag name cannot be empty');
|
||||
} else if (name.includes(' ')) {
|
||||
setTagNameError('Tag name cannot contain spaces');
|
||||
} else {
|
||||
// eslint-disable-next-line unicorn/no-useless-undefined
|
||||
setTagNameError(undefined);
|
||||
}
|
||||
};
|
||||
const lastValidTagColor = useRef(tagColor.parsed);
|
||||
|
||||
const handleTagColorChange = (color: string) => {
|
||||
setTagColor(color);
|
||||
|
||||
if (isValidColor(color)) {
|
||||
setValidatedTagColor(color);
|
||||
// eslint-disable-next-line unicorn/no-useless-undefined
|
||||
setTagColorError(undefined);
|
||||
} else {
|
||||
setTagColorError('Color must be a valid hex or rgb value');
|
||||
}
|
||||
setTagColor(validateColor(color));
|
||||
if (tagColor.valid) lastValidTagColor.current = tagColor.parsed;
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<TagPreview name={tagName} color={validatedTagColor} />
|
||||
<TagPreview name={tagName.parsed} color={lastValidTagColor.current} />
|
||||
<TextInput
|
||||
mode="outlined"
|
||||
label="Name"
|
||||
value={tagName}
|
||||
onChangeText={handleTagNameChange}
|
||||
error={!!tagNameError}
|
||||
value={tagName.raw}
|
||||
onChangeText={name => setTagName(validateTagName(name))}
|
||||
error={!tagName.valid}
|
||||
autoCapitalize="none"
|
||||
selectTextOnFocus
|
||||
/>
|
||||
<HelperText type="error" visible={!!tagNameError}>
|
||||
{tagNameError}
|
||||
<HelperText type="error" visible={!tagName.valid}>
|
||||
{tagName.error}
|
||||
</HelperText>
|
||||
<TextInput
|
||||
mode="outlined"
|
||||
label="Color"
|
||||
value={tagColor}
|
||||
value={tagColor.raw}
|
||||
onChangeText={handleTagColorChange}
|
||||
error={!!tagColorError}
|
||||
error={!tagColor.valid}
|
||||
autoCorrect={false}
|
||||
right={
|
||||
<TextInput.Icon
|
||||
@@ -80,8 +55,8 @@ const TagEditor = ({
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<HelperText type="error" visible={!!tagColorError}>
|
||||
{tagColorError}
|
||||
<HelperText type="error" visible={!tagColor.valid}>
|
||||
{tagColor.error}
|
||||
</HelperText>
|
||||
</>
|
||||
);
|
||||
|
Reference in New Issue
Block a user