mirror of
https://github.com/ecency/ecency-mobile.git
synced 2024-12-03 00:15:14 +03:00
support for add and update snippets
This commit is contained in:
parent
6291b92bb2
commit
0f194a5670
@ -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<Snippet>)=>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<string|null>(null);
|
||||
const [snippetId, setSnippetId] = useState<string | null>(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 = (
|
||||
<ThemeContainer>
|
||||
{({isDarkTheme})=>(
|
||||
<KeyboardAvoidingView
|
||||
{({ isDarkTheme }) => (
|
||||
<KeyboardAvoidingView
|
||||
style={styles.container}
|
||||
keyboardVerticalOffset={Platform.OS == 'ios' ? 64 : null}
|
||||
behavior={Platform.OS === 'ios' ? 'padding' : null}
|
||||
>
|
||||
<View style={styles.inputContainer}>
|
||||
|
||||
|
||||
<View style={{height:Math.max(35, titleHeight)}}>
|
||||
|
||||
<View style={{ height: Math.max(35, titleHeight) }}>
|
||||
<TextInput
|
||||
autoFocus={true}
|
||||
innerRef={titleInputRef}
|
||||
@ -97,23 +89,23 @@ const SnippetEditorModal = ({onSnippetsUpdated}: SnippetEditorModalProps, ref) =
|
||||
height={Math.max(35, titleHeight)}
|
||||
placeholderTextColor={isDarkTheme ? '#526d91' : '#c1c5c7'}
|
||||
maxLength={250}
|
||||
placeholder={intl.formatMessage({id:'snippets.placeholder_title'})}
|
||||
placeholder={intl.formatMessage({ id: 'snippets.placeholder_title' })}
|
||||
multiline
|
||||
numberOfLines={2}
|
||||
onContentSizeChange={(event) => {
|
||||
setTitleHeight(event.nativeEvent.contentSize.height);
|
||||
setTitleHeight(event.nativeEvent.contentSize.height);
|
||||
}}
|
||||
onChangeText={setTitle}
|
||||
value={title}
|
||||
/>
|
||||
</View>
|
||||
|
||||
|
||||
<TextInput
|
||||
multiline
|
||||
autoCorrect={true}
|
||||
value={body}
|
||||
onChangeText={setBody}
|
||||
placeholder={intl.formatMessage({id:'snippets.placeholder_body'})}
|
||||
placeholder={intl.formatMessage({ id: 'snippets.placeholder_body' })}
|
||||
placeholderTextColor={isDarkTheme ? '#526d91' : '#c1c5c7'}
|
||||
selectionColor="#357ce6"
|
||||
style={styles.bodyWrapper}
|
||||
@ -125,43 +117,43 @@ const SnippetEditorModal = ({onSnippetsUpdated}: SnippetEditorModalProps, ref) =
|
||||
/>
|
||||
</View>
|
||||
|
||||
|
||||
<View style={styles.actionPanel}>
|
||||
<TextButton
|
||||
text={intl.formatMessage({id:'snippets.btn_close'})}
|
||||
onPress={()=>setShowModal(false)}
|
||||
style={styles.closeButton}
|
||||
/>
|
||||
<TextButton
|
||||
text={intl.formatMessage({id:'snippets.btn_save'})}
|
||||
onPress={_saveSnippet}
|
||||
textStyle={styles.btnText}
|
||||
style={styles.saveButton}
|
||||
/>
|
||||
</View>
|
||||
|
||||
|
||||
<View style={styles.actionPanel}>
|
||||
<TextButton
|
||||
text={intl.formatMessage({ id: 'snippets.btn_close' })}
|
||||
onPress={() => setShowModal(false)}
|
||||
style={styles.closeButton}
|
||||
/>
|
||||
<TextButton
|
||||
text={intl.formatMessage({ id: 'snippets.btn_save' })}
|
||||
onPress={_saveSnippet}
|
||||
textStyle={styles.btnText}
|
||||
style={styles.saveButton}
|
||||
/>
|
||||
</View>
|
||||
|
||||
</KeyboardAvoidingView>
|
||||
)}
|
||||
</ThemeContainer>
|
||||
)
|
||||
</ThemeContainer>
|
||||
)
|
||||
|
||||
return (
|
||||
<Modal
|
||||
isOpen={showModal}
|
||||
handleOnModalClose={()=>{setShowModal(false)}}
|
||||
presentationStyle="formSheet"
|
||||
title={intl.formatMessage({
|
||||
id:isNewSnippet
|
||||
? 'snippets.title_add_snippet'
|
||||
: 'snippets.title_edit_snippet'
|
||||
})}
|
||||
animationType="slide"
|
||||
style={styles.modalStyle}
|
||||
>
|
||||
{_renderContent}
|
||||
</Modal>
|
||||
|
||||
);
|
||||
return (
|
||||
<Modal
|
||||
isOpen={showModal}
|
||||
handleOnModalClose={() => { setShowModal(false) }}
|
||||
presentationStyle="formSheet"
|
||||
title={intl.formatMessage({
|
||||
id: isNewSnippet
|
||||
? 'snippets.title_add_snippet'
|
||||
: 'snippets.title_edit_snippet'
|
||||
})}
|
||||
animationType="slide"
|
||||
style={styles.modalStyle}
|
||||
>
|
||||
{_renderContent}
|
||||
</Modal>
|
||||
|
||||
);
|
||||
};
|
||||
|
||||
export default forwardRef(SnippetEditorModal);
|
||||
|
@ -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 (
|
||||
<View style={[styles.itemWrapper, index % 2 !== 0 && styles.itemWrapperGray]}>
|
||||
<View style={styles.itemHeader}>
|
||||
<Text style={styles.title} numberOfLines={1} >{`${title}`}</Text>
|
||||
<IconButton
|
||||
iconStyle={styles.itemIcon}
|
||||
style={styles.itemIconWrapper}
|
||||
iconType="MaterialCommunityIcons"
|
||||
name="pencil"
|
||||
onPress={onEditPress}
|
||||
size={20}
|
||||
/>
|
||||
<IconButton
|
||||
iconStyle={styles.itemIcon}
|
||||
style={styles.itemIconWrapper}
|
||||
iconType="MaterialCommunityIcons"
|
||||
name="delete"
|
||||
onPress={onRemovePress}
|
||||
size={20}
|
||||
/>
|
||||
{
|
||||
id && (
|
||||
<>
|
||||
<IconButton
|
||||
iconStyle={styles.itemIcon}
|
||||
style={styles.itemIconWrapper}
|
||||
iconType="MaterialCommunityIcons"
|
||||
name="pencil"
|
||||
onPress={onEditPress}
|
||||
size={20}
|
||||
/>
|
||||
<IconButton
|
||||
iconStyle={styles.itemIcon}
|
||||
style={styles.itemIconWrapper}
|
||||
iconType="MaterialCommunityIcons"
|
||||
name="delete"
|
||||
onPress={onRemovePress}
|
||||
size={20}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
</View>
|
||||
|
||||
|
||||
<Text style={styles.body} numberOfLines={2} ellipsizeMode="tail">{`${body}`}</Text>
|
||||
|
||||
|
||||
</View>
|
||||
)
|
||||
};
|
||||
|
@ -69,6 +69,7 @@ const SnippetsModal = ({ handleOnSelect }: SnippetsModalProps) => {
|
||||
return (
|
||||
<TouchableOpacity onPress={_onPress}>
|
||||
<SnippetItem
|
||||
id={item.id}
|
||||
title={item.title}
|
||||
body={item.body}
|
||||
index={index}
|
||||
@ -123,7 +124,7 @@ const SnippetsModal = ({ handleOnSelect }: SnippetsModalProps) => {
|
||||
ListEmptyComponent={_renderEmptyContent}
|
||||
refreshControl={
|
||||
<RefreshControl
|
||||
refreshing={snippetsQuery.isLoading}
|
||||
refreshing={snippetsQuery.isFetching}
|
||||
onRefresh={snippetsQuery.refetch}
|
||||
/>
|
||||
}
|
||||
@ -133,9 +134,6 @@ const SnippetsModal = ({ handleOnSelect }: SnippetsModalProps) => {
|
||||
|
||||
<SnippetEditorModal
|
||||
ref={editorRef}
|
||||
onSnippetsUpdated={() => {
|
||||
throw new Error('not implemented');
|
||||
}}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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<Snippet[]>([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<Snippet[], undefined, SnippetMutationVars>(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<Snippet[]>([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' })));
|
||||
}
|
||||
})
|
||||
}
|
Loading…
Reference in New Issue
Block a user