Refactor validation

Signed-off-by: Nikolaos Karaolidis <nick@karaolidis.com>
This commit is contained in:
2023-07-21 16:34:44 +03:00
parent b7dd1c77af
commit 3c303e0304
17 changed files with 256 additions and 151 deletions

View File

@@ -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))
}
/>
</>
);

View File

@@ -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>
)}
/>

View File

@@ -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>
</>
);