diff --git a/src/components/snippetEditorModal/snippetEditorModal.tsx b/src/components/snippetEditorModal/snippetEditorModal.tsx index e5935443a..2a0a8f2e5 100644 --- a/src/components/snippetEditorModal/snippetEditorModal.tsx +++ b/src/components/snippetEditorModal/snippetEditorModal.tsx @@ -4,92 +4,84 @@ import { Alert, KeyboardAvoidingView, Platform, View } from 'react-native'; import { TextInput } from '..'; import { ThemeContainer } from '../../containers'; import { Snippet } from '../../models'; -import { addFragment, updateFragment} from '../../providers/ecency/ecency'; +import { useSnippetsMutation } from '../../providers/queries'; import { TextButton } from '../buttons'; import Modal from '../modal'; import styles from './snippetEditorModalStyles'; export interface SnippetEditorModalRef { - showNewModal:()=>void; - showEditModal:(snippet:Snippet)=>void; + showNewModal: () => void; + showEditModal: (snippet: Snippet) => void; } -interface SnippetEditorModalProps { - onSnippetsUpdated:(snips:Array)=>void; -} -const SnippetEditorModal = ({onSnippetsUpdated}: SnippetEditorModalProps, ref) => { +const SnippetEditorModal = ({}, ref) => { const intl = useIntl(); const titleInputRef = useRef(null); const bodyInputRef = useRef(null); + const snippetsMutation = useSnippetsMutation(); + const [title, setTitle] = useState(''); const [body, setBody] = useState(''); - const [snippetId, setSnippetId] = useState(null); + const [snippetId, setSnippetId] = useState(null); const [isNewSnippet, setIsNewSnippet] = useState(true); - const [showModal, setShowModal] = useState(false); + const [showModal, setShowModal] = useState(false); const [titleHeight, setTitleHeight] = useState(0) useImperativeHandle(ref, () => ({ showNewModal: () => { - setTitle(''); - setBody(''); - setIsNewSnippet(true); - setShowModal(true); + setTitle(''); + setBody(''); + setIsNewSnippet(true); + setShowModal(true); }, - showEditModal:(snippet:Snippet)=>{ + showEditModal: (snippet: Snippet) => { setSnippetId(snippet.id); setTitle(snippet.title); setBody(snippet.body); setIsNewSnippet(false); setShowModal(true); } - })); + })); //save snippet based on editor type const _saveSnippet = async () => { - try{ - if(!title || !body){ - Alert.alert(intl.formatMessage({id:'snippets.message_incomplete'})); + + if (!title || !body) { + Alert.alert(intl.formatMessage({ id: 'snippets.message_incomplete' })); return; } - let response = []; - if(!isNewSnippet){ - console.log("Updating snippet:", snippetId, title, body) - response = await updateFragment(snippetId, title, body); - console.log("Response from add snippet: ", response) - }else{ - console.log("Saving snippet:", title, body) - const res = await addFragment(title, body) - response = res && res.fragments - console.log("Response from add snippet: ", response) - } - setShowModal(false); - onSnippetsUpdated(response); - }catch(err){ - Alert.alert(intl.formatMessage({id:'snippets.message_failed'})) - console.warn("Failed to save snippet", err) - } - + console.log("Saving snippet:", title, body) + + snippetsMutation.mutate({ + id: isNewSnippet ? null : snippetId, + title, + body + }) + + + setShowModal(false); + } const _renderContent = ( - {({isDarkTheme})=>( - ( + - - + + { - setTitleHeight(event.nativeEvent.contentSize.height); + setTitleHeight(event.nativeEvent.contentSize.height); }} onChangeText={setTitle} value={title} /> - + - - - setShowModal(false)} - style={styles.closeButton} - /> - - - + + + setShowModal(false)} + style={styles.closeButton} + /> + + + )} - - ) + + ) - return ( - {setShowModal(false)}} - presentationStyle="formSheet" - title={intl.formatMessage({ - id:isNewSnippet - ? 'snippets.title_add_snippet' - : 'snippets.title_edit_snippet' - })} - animationType="slide" - style={styles.modalStyle} - > - {_renderContent} - - - ); + return ( + { setShowModal(false) }} + presentationStyle="formSheet" + title={intl.formatMessage({ + id: isNewSnippet + ? 'snippets.title_add_snippet' + : 'snippets.title_edit_snippet' + })} + animationType="slide" + style={styles.modalStyle} + > + {_renderContent} + + + ); }; export default forwardRef(SnippetEditorModal); diff --git a/src/components/snippetsModal/snippetItem.tsx b/src/components/snippetsModal/snippetItem.tsx index 03fc755c0..a3e9ecce8 100644 --- a/src/components/snippetsModal/snippetItem.tsx +++ b/src/components/snippetsModal/snippetItem.tsx @@ -4,38 +4,46 @@ import IconButton from '../iconButton'; import styles from './snippetsModalStyles'; interface SnippetItemProps { - title:string; - body:string; - index:number; - onEditPress:()=>void; - onRemovePress:()=>void; + id: string | null; + title: string; + body: string; + index: number; + onEditPress: () => void; + onRemovePress: () => void; } -const SnippetItem = ({title, body, index, onEditPress, onRemovePress}: SnippetItemProps) => { +const SnippetItem = ({ id, title, body, index, onEditPress, onRemovePress }: SnippetItemProps) => { return ( {`${title}`} - - + { + id && ( + <> + + + + ) + } + - + {`${body}`} - + ) }; diff --git a/src/components/snippetsModal/snippetsModal.tsx b/src/components/snippetsModal/snippetsModal.tsx index 44287c2f3..160ad497d 100644 --- a/src/components/snippetsModal/snippetsModal.tsx +++ b/src/components/snippetsModal/snippetsModal.tsx @@ -69,6 +69,7 @@ const SnippetsModal = ({ handleOnSelect }: SnippetsModalProps) => { return ( { ListEmptyComponent={_renderEmptyContent} refreshControl={ } @@ -133,9 +134,6 @@ const SnippetsModal = ({ handleOnSelect }: SnippetsModalProps) => { { - throw new Error('not implemented'); - }} /> ); diff --git a/src/providers/ecency/ecency.ts b/src/providers/ecency/ecency.ts index eddb80bed..86aa55419 100644 --- a/src/providers/ecency/ecency.ts +++ b/src/providers/ecency/ecency.ts @@ -18,6 +18,7 @@ import { ReceivedVestingShare, Referral, ReferralStat, + Snippet, } from './ecency.types'; /** @@ -326,7 +327,7 @@ export const deleteFavorite = async (targetUsername: string) => { export const getFragments = async () => { try { const response = await ecencyApi.post('/private-api/fragments'); - return response.data; + return response.data as Snippet[]; } catch (error) { console.warn('Failed to get fragments', error); bugsnagInstance.notify(error); diff --git a/src/providers/ecency/ecency.types.ts b/src/providers/ecency/ecency.types.ts index 571cfe098..fff31720b 100644 --- a/src/providers/ecency/ecency.types.ts +++ b/src/providers/ecency/ecency.types.ts @@ -1,26 +1,35 @@ import { QuoteItem } from "../../redux/reducers/walletReducer"; export interface ReceivedVestingShare { - delegator:string; - delegatee:string; - vesting_shares:string; - timestamp:string; + delegator: string; + delegatee: string; + vesting_shares: string; + timestamp: string; +} + + +export interface Snippet { + id: string; + title: string; + body: string; + created: string; + modified: string; } export interface EcencyUser { - username:string; - points:string; - unclaimed_points:string; - points_by_type:{[key:string]:string}; - unclaimed_points_by_type:{[key:string]:string}; + username: string; + points: string; + unclaimed_points: string; + points_by_type: { [key: string]: string }; + unclaimed_points_by_type: { [key: string]: string }; } export interface Referral { - id:number; - referral:string; - rewarded:boolean; - username:string; - created:string + id: number; + referral: string; + rewarded: boolean; + username: string; + created: string } export interface ReferralStat { @@ -32,7 +41,7 @@ export interface UserPoint { id: number; type: number; amount: string; - created:string; + created: string; memo?: string; receiver?: string; sender?: string; @@ -40,14 +49,14 @@ export interface UserPoint { } export interface LatestQuotes { - [key:string]:QuoteItem + [key: string]: QuoteItem } export interface CommentHistoryItem { body: string; tags: [string]; title: string; - timestamp:string; + timestamp: string; v: number; } diff --git a/src/providers/queries/editorQueries.ts b/src/providers/queries/editorQueries.ts index 6b068b083..e6712129e 100644 --- a/src/providers/queries/editorQueries.ts +++ b/src/providers/queries/editorQueries.ts @@ -1,16 +1,72 @@ -import { useQuery } from '@tanstack/react-query'; +import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; import { useIntl } from 'react-intl'; import { useAppDispatch } from '../../hooks'; import { toastNotification } from '../../redux/actions/uiAction'; -import { getFragments } from '../ecency/ecency'; +import { addFragment, getFragments, updateFragment } from '../ecency/ecency'; +import { Snippet } from '../ecency/ecency.types'; import QUERIES from './queryKeys'; +interface SnippetMutationVars { + id: string | null; + title: string; + body: string; +} + export const useSnippetsQuery = () => { - const intl = useIntl(); - const dispatch = useAppDispatch(); - return useQuery([QUERIES.SNIPPETS.GET], getFragments, { - onError: () => { - dispatch(toastNotification(intl.formatMessage({ id: 'alert.fail' }))); - }, - }); + const intl = useIntl(); + const dispatch = useAppDispatch(); + return useQuery([QUERIES.SNIPPETS.GET], getFragments, { + onError: () => { + dispatch(toastNotification(intl.formatMessage({ id: 'alert.fail' }))); + }, + }); }; + + +export const useSnippetsMutation = () => { + const intl = useIntl(); + const dispatch = useAppDispatch(); + const queryClient = useQueryClient(); + + return useMutation(async (vars) => { + console.log("going to add/update snippet", vars); + if (vars.id) { + const response = await updateFragment(vars.id, vars.title, vars.body) + return response; + } else { + const response = await addFragment(vars.title, vars.body) + return response; + } + }, { + onMutate: (vars) => { + console.log("mutate snippets for add/update", vars) + + const _newItem = { + id: vars.id, + title: vars.title, + body: vars.body, + created: new Date().toDateString(), + modified: new Date().toDateString(), + } as Snippet; + + const data = queryClient.getQueryData([QUERIES.SNIPPETS.GET]); + + let _newData: Snippet[] = data ? [...data] : []; + if (vars.id) { + const snipIndex = _newData.findIndex((item) => vars.id === item.id) + _newData[snipIndex] = _newItem; + } else { + _newData = [_newItem, ..._newData]; + } + + queryClient.setQueryData([QUERIES.SNIPPETS.GET], _newData) + }, + onSuccess: (data) => { + console.log("added/updated snippet", data) + queryClient.invalidateQueries([QUERIES.SNIPPETS.GET]) + }, + onError: () => { + dispatch(toastNotification(intl.formatMessage({ id: 'snippets.message_failed' }))); + } + }) +} \ No newline at end of file