diff --git a/app/CharacterEditor.tsx b/app/CharacterEditor.tsx index 02ff51f..00a2393 100644 --- a/app/CharacterEditor.tsx +++ b/app/CharacterEditor.tsx @@ -1,20 +1,27 @@ +import ThemedButton from '@components/buttons/ThemedButton' +import ThemedTextInput from '@components/input/ThemedTextInput' import Alert from '@components/views/Alert' import Avatar from '@components/views/Avatar' import FadeDownView from '@components/views/FadeDownView' +import HeaderTitle from '@components/views/HeaderTitle' import PopupMenu from '@components/views/PopupMenu' import { AntDesign } from '@expo/vector-icons' import { Tokenizer } from '@lib/engine/Tokenizer' import { useViewerState } from '@lib/state/AvatarViewer' import { CharacterCardData } from '@lib/state/Characters' -import { Characters, Chats, Logger, Style } from '@lib/utils/Global' +import { Theme } from '@lib/theme/ThemeManager' +import { Characters, Chats, Logger } from '@lib/utils/Global' +import { usePreventRemove } from '@react-navigation/core' import AvatarViewer from '@screens/ChatMenu/ChatWindow/AvatarViewer' import * as DocumentPicker from 'expo-document-picker' -import { Stack, useNavigation, useRouter } from 'expo-router' -import { useEffect, useRef, useState } from 'react' -import { ScrollView, StyleSheet, Text, TextInput, TouchableOpacity, View } from 'react-native' +import { useNavigation, useRouter } from 'expo-router' +import { useEffect, useState } from 'react' +import { ScrollView, StyleSheet, Text, TouchableOpacity, View } from 'react-native' import { useShallow } from 'zustand/react/shallow' const ChracterEditor = () => { + const styles = useStyles() + const { color, spacing } = Theme.useTheme() const router = useRouter() const navigation = useNavigation() const { currentCard, setCurrentCard, charId, charName, unloadCharacter } = @@ -30,16 +37,18 @@ const ChracterEditor = () => { const getTokenCount = Tokenizer.useTokenizer((state) => state.getTokenCount) const [characterCard, setCharacterCard] = useState(currentCard) - const { chat, unloadChat } = Chats.useChat() const setShowViewer = useViewerState((state) => state.setShow) - const [edited, setEdited] = useState(false) - const [altSwipeIndex, setAltSwipeIndex] = useState(0) - const editedBackAction = (exitCallback: () => void) => { + const setCharacterCardEdited = (card: CharacterCardData) => { + if (!edited) setEdited(true) + setCharacterCard(card) + } + + usePreventRemove(edited, ({ data }) => { Alert.alert({ title: `Unsaved Changes`, description: `You have unsaved changes, leaving now will discard your progress.`, @@ -48,63 +57,31 @@ const ChracterEditor = () => { { label: 'Save', onPress: async () => { - await savecard() - if (!chat) unloadCharacter() - exitCallback() + await handleSaveCard() + navigation.dispatch(data.action) }, }, { label: 'Discard Changes', onPress: () => { - if (!chat) unloadCharacter() - exitCallback() + navigation.dispatch(data.action) }, type: 'warning', }, ], }) - return true - } - - const defaultListener = () => { - const removeListener = navigation.addListener('beforeRemove', (e) => { - if (!chat) unloadCharacter() - navigation.dispatch(e.data.action) - }) - return removeListener - } - - const removeListener = useRef(defaultListener()) - const firstRender = useRef(true) + }) - useEffect(() => { - if (firstRender.current) { - firstRender.current = false - return - } - setEdited(true) - removeListener.current() - removeListener.current = navigation.addListener('beforeRemove', (e) => { - e.preventDefault() - editedBackAction(() => { - if (!chat) unloadCharacter() - navigation.dispatch(e.data.action) - }) - }) - }, [characterCard]) - - const savecard = async () => { + const handleSaveCard = async () => { if (characterCard && charId) return Characters.db.mutate.updateCard(characterCard, charId).then(() => { setCurrentCard(charId) setEdited(() => false) - removeListener.current() - removeListener.current = defaultListener() Logger.log('Card Saved!', true) }) } - const deleteCard = () => { + const handleDeleteCard = () => { Alert.alert({ title: `Delete Character`, description: `Are you sure you want to delete '${charName}'? This cannot be undone.`, @@ -124,6 +101,12 @@ const ChracterEditor = () => { }) } + useEffect(() => { + return () => { + if (!chat) unloadCharacter() + } + }, []) + const handleDeleteImage = () => { Alert.alert({ title: `Delete Image`, @@ -162,7 +145,7 @@ const ChracterEditor = () => { ...(characterCard?.alternate_greetings ?? []), { id: id, greeting: '', character_id: charId }, ] - setCharacterCard({ ...characterCard, alternate_greetings: greetings }) + setCharacterCardEdited({ ...characterCard, alternate_greetings: greetings }) if (characterCard.alternate_greetings.length !== 0) { setAltSwipeIndex(altSwipeIndex + 1) } @@ -180,7 +163,7 @@ const ChracterEditor = () => { (item) => item.id !== id ) setAltSwipeIndex(0) - setCharacterCard({ ...characterCard, alternate_greetings: greetings }) + setCharacterCardEdited({ ...characterCard, alternate_greetings: greetings }) } const handleDeleteAltMessage = async () => { @@ -202,14 +185,12 @@ const ChracterEditor = () => { return ( - + {characterCard && ( - + { /> - - - Delete Character - + + {edited && ( - - - Save - + )} - { - setCharacterCard({ + setCharacterCardEdited({ ...characterCard, name: mes, }) @@ -289,17 +263,13 @@ const ChracterEditor = () => { - - Description Tokens: {getTokenCount(characterCard?.description ?? '')} - - - { - setCharacterCard({ + setCharacterCardEdited({ ...characterCard, description: mes, }) @@ -307,13 +277,11 @@ const ChracterEditor = () => { value={characterCard?.description} /> - First Message - - { - setCharacterCard({ + setCharacterCardEdited({ ...characterCard, first_mes: mes, }) @@ -326,15 +294,13 @@ const ChracterEditor = () => { style={{ flexDirection: 'row', justifyContent: 'space-between', - marginTop: 24, - marginBottom: 12, }}> - - Alternate Greetings{' '} + + Alternate Greetings ({characterCard.alternate_greetings.length}) {characterCard.alternate_greetings.length !== 0 && ( {altSwipeIndex + 1} / {characterCard.alternate_greetings.length} @@ -344,19 +310,13 @@ const ChracterEditor = () => { {characterCard.alternate_greetings.length !== 0 && ( - + )} setAltSwipeIndex(Math.max(altSwipeIndex - 1, 0))}> @@ -364,11 +324,7 @@ const ChracterEditor = () => { {altSwipeIndex === characterCard.alternate_greetings.length - 1 || characterCard.alternate_greetings.length === 0 ? ( - + ) : ( { ) ) }> - + )} {characterCard.alternate_greetings.length !== 0 ? ( - { const greetings = [...characterCard.alternate_greetings] greetings[altSwipeIndex].greeting = mes - setCharacterCard({ + setCharacterCardEdited({ ...characterCard, alternate_greetings: greetings, }) @@ -410,22 +361,19 @@ const ChracterEditor = () => { ) : ( No Alternate Greetings )} - Personality - - { - setCharacterCard({ + setCharacterCardEdited({ ...characterCard, personality: mes, }) @@ -433,13 +381,11 @@ const ChracterEditor = () => { value={characterCard?.personality} /> - Scenario - - { - setCharacterCard({ + setCharacterCardEdited({ ...characterCard, scenario: mes, }) @@ -448,13 +394,11 @@ const ChracterEditor = () => { numberOfLines={3} /> - Example Messages - - { - setCharacterCard({ + setCharacterCardEdited({ ...characterCard, mes_example: mes, }) @@ -468,97 +412,53 @@ const ChracterEditor = () => { ) } -const styles = StyleSheet.create({ - mainContainer: { - flex: 1, - paddingHorizontal: 16, - paddingVertical: 16, - }, - - button: { - marginRight: 20, - }, - - characterHeader: { - alignContent: 'flex-start', - borderRadius: 16, - flexDirection: 'row', - alignItems: 'center', - }, - - characterHeaderInfo: { - marginLeft: 24, - rowGap: 12, - flex: 1, - }, - - buttonContainer: { - justifyContent: 'flex-start', - flexDirection: 'row', - columnGap: 4, - }, - - foregroundButton: { - flexGrow: 1, - flexDirection: 'row', - borderColor: Style.getColor('confirm-brand'), - backgroundColor: Style.getColor('confirm-brand'), - borderWidth: 1, - padding: 8, - borderRadius: 4, - columnGap: 8, - }, - - buttonDestructive: { - flexGrow: 1, - flexDirection: 'row', - borderColor: Style.getColor('destructive-brand'), - backgroundColor: Style.getColor('destructive-brand'), - borderWidth: 1, - padding: 8, - borderRadius: 4, - columnGap: 8, - }, - - buttonText: { - color: Style.getColor('primary-text1'), - }, - - avatar: { - width: 80, - height: 80, - borderRadius: 20, - borderColor: Style.getColor('primary-brand'), - borderWidth: 2, - }, - - boxText: { - color: Style.getColor('primary-text2'), - paddingTop: 24, - paddingBottom: 12, - }, - - input: { - color: Style.getColor('primary-text1'), - textAlignVertical: 'top', - paddingVertical: 8, - backgroundColor: Style.getColor('primary-surface1'), - borderColor: Style.getColor('primary-brand'), - borderRadius: 8, - borderWidth: 1, - paddingHorizontal: 8, - }, - - editHover: { - position: 'absolute', - left: '75%', - top: '75%', - padding: 8, - borderColor: Style.getColor('primary-text3'), - borderWidth: 1, - backgroundColor: Style.getColor('primary-surface3'), - borderRadius: 12, - }, -}) +const useStyles = () => { + const { color, spacing } = Theme.useTheme() + return StyleSheet.create({ + mainContainer: { + flex: 1, + paddingHorizontal: spacing.xl, + paddingVertical: spacing.xl, + }, + + characterHeader: { + alignContent: 'flex-start', + borderRadius: spacing.xl, + flexDirection: 'row', + alignItems: 'center', + }, + + characterHeaderInfo: { + marginLeft: spacing.xl2, + rowGap: 12, + flex: 1, + }, + + buttonContainer: { + justifyContent: 'flex-start', + flexDirection: 'row', + columnGap: 4, + }, + + avatar: { + width: 80, + height: 80, + borderRadius: spacing.xl2, + borderColor: color.primary._500, + borderWidth: 2, + }, + + editHover: { + position: 'absolute', + left: '75%', + top: '75%', + padding: spacing.m, + borderColor: color.text._700, + borderWidth: 1, + backgroundColor: color.primary._300, + borderRadius: spacing.l, + }, + }) +} export default ChracterEditor diff --git a/app/Instruct.tsx b/app/Instruct.tsx index 20c4263..fa8ff57 100644 --- a/app/Instruct.tsx +++ b/app/Instruct.tsx @@ -189,7 +189,10 @@ const Instruct = () => { - + { numberOfLines={5} multiline /> - + { multiline /> - + { multiline /> - + = ({ onPressOut, variant = 'primary', iconName = undefined, - iconSize = 24, + iconSize = 20, iconStyle = undefined, icon = undefined, ...rest diff --git a/app/components/input/StringArrayEditor.tsx b/app/components/input/StringArrayEditor.tsx index e69721f..a959823 100644 --- a/app/components/input/StringArrayEditor.tsx +++ b/app/components/input/StringArrayEditor.tsx @@ -89,8 +89,6 @@ const useStyles = () => { borderWidth: 1, color: color.text._100, borderColor: color.neutral._300, - marginBottom: spacing.s, - marginRight: spacing.l, paddingHorizontal: spacing.s, paddingVertical: spacing.m, borderRadius: spacing.m, @@ -116,7 +114,7 @@ const useStyles = () => { tag: { borderColor: color.primary._700, borderWidth: 1, - paddingVertical: 6, + paddingVertical: 8, paddingLeft: 12, paddingRight: 8, borderRadius: 8, @@ -139,7 +137,7 @@ const useStyles = () => { input: { flex: 1, color: color.text._100, - paddingVertical: 6, + paddingVertical: 4, paddingHorizontal: 8, borderRadius: 8, }, diff --git a/app/components/input/ThemedTextInput.tsx b/app/components/input/ThemedTextInput.tsx index 61398b0..d967867 100644 --- a/app/components/input/ThemedTextInput.tsx +++ b/app/components/input/ThemedTextInput.tsx @@ -18,32 +18,32 @@ const ThemedTextInput: React.FC = ({ return ( - - {label} - + {label && ( + + {label} + + )} 1) || multiline} numberOfLines={numberOfLines} style={{ color: color.text._100, borderColor: color.neutral._300, borderWidth: 1, - paddingVertical: 4, - paddingHorizontal: 8, - marginVertical: 8, - marginHorizontal: 4, + paddingVertical: 8, + paddingHorizontal: 12, borderRadius: 8, textAlignVertical: numberOfLines && numberOfLines > 1 ? `top` : `center`, }} placeholder="----" placeholderTextColor={color.text._400} + {...rest} /> ) diff --git a/lib/theme/ThemeColor.ts b/lib/theme/ThemeColor.ts index b2deec6..0137ac4 100644 --- a/lib/theme/ThemeColor.ts +++ b/lib/theme/ThemeColor.ts @@ -87,7 +87,7 @@ export namespace DefaultColorSchemes { _600: '#6e6e6e', _700: '#505050', _800: '#323232', - _900: '#141414', + _900: '#000000', }, }