mirror of
https://github.com/ecency/ecency-mobile.git
synced 2024-12-03 11:40:44 +03:00
Merge pull request #2178 from ecency/nt/multiple-media-selection
Nt/multiple media selection
This commit is contained in:
commit
2d96bd449b
@ -1,4 +1,4 @@
|
||||
import React, { useState } from 'react';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { View, TouchableOpacity } from 'react-native';
|
||||
|
||||
import styles from './checkboxStyles';
|
||||
@ -6,6 +6,10 @@ import styles from './checkboxStyles';
|
||||
const CheckBoxView = ({ clicked, value, isChecked, style, locked }) => {
|
||||
const [isCheck, setIsCheck] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
setIsCheck(isChecked);
|
||||
}, [isChecked]);
|
||||
|
||||
const _checkClicked = () => {
|
||||
setIsCheck(!isCheck);
|
||||
|
||||
|
23
src/components/markdownEditor/view/formats/applyMediaLink.ts
Normal file
23
src/components/markdownEditor/view/formats/applyMediaLink.ts
Normal file
@ -0,0 +1,23 @@
|
||||
import {replaceBetween } from './utils';
|
||||
|
||||
export default async ({ text, selection, setTextAndSelection, items }) => {
|
||||
const imagePrefix = '!';
|
||||
|
||||
let newText = text;
|
||||
let newSelection = selection;
|
||||
|
||||
items.forEach(item => {
|
||||
if(item.url && item.text){
|
||||
const formatedText = `\n${imagePrefix}[${item.text}](${item.url})\n`
|
||||
newText = replaceBetween(newText, newSelection, formatedText);
|
||||
const newIndex = newText && newText.indexOf(item.url, newSelection.start) + item.url.length + 2;
|
||||
newSelection = {
|
||||
start: newIndex,
|
||||
end: newIndex
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
setTextAndSelection({ text: newText, selection: newSelection });
|
||||
};
|
@ -16,7 +16,7 @@ import { Icon } from '../../icon';
|
||||
|
||||
// Utils
|
||||
import Formats from './formats/formats';
|
||||
import applyImageLink from './formats/applyWebLinkFormat';
|
||||
import applyMediaLink from './formats/applyMediaLink';
|
||||
|
||||
// Actions
|
||||
import { toggleAccountsBottomSheet } from '../../../redux/actions/uiAction';
|
||||
@ -147,12 +147,11 @@ const MarkdownEditorView = ({
|
||||
useEffect(() => {
|
||||
if (uploadedImage && uploadedImage.url) {
|
||||
if (uploadedImage.shouldInsert) {
|
||||
applyImageLink({
|
||||
applyMediaLink({
|
||||
text,
|
||||
selection,
|
||||
setTextAndSelection: _setTextAndSelection,
|
||||
item: { url: uploadedImage.url, text: uploadedImage.hash },
|
||||
isImage: !!uploadedImage,
|
||||
items: [{ url: uploadedImage.url, text: uploadedImage.hash }],
|
||||
});
|
||||
} else {
|
||||
uploadsGalleryModalRef.current.showModal();
|
||||
@ -265,14 +264,18 @@ const MarkdownEditorView = ({
|
||||
});
|
||||
};
|
||||
|
||||
const _handleOnMediaSelect = (mediaInsert) => {
|
||||
if (mediaInsert && mediaInsert.url) {
|
||||
applyImageLink({
|
||||
const _handleOnMediaSelect = (mediaArray) => {
|
||||
const items = mediaArray.map((mediaInsert) => ({
|
||||
url: mediaInsert.url,
|
||||
text: mediaInsert.hash,
|
||||
}));
|
||||
|
||||
if (items.length) {
|
||||
applyMediaLink({
|
||||
text,
|
||||
selection,
|
||||
setTextAndSelection: _setTextAndSelection,
|
||||
item: { url: mediaInsert.url, text: mediaInsert.hash },
|
||||
isImage: !!mediaInsert,
|
||||
items,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
@ -2,12 +2,13 @@ import React, { forwardRef, useEffect, useImperativeHandle, useState } from 'rea
|
||||
import { useIntl } from 'react-intl';
|
||||
import {Text, View, FlatList, RefreshControl, TouchableOpacity, Alert, Platform } from 'react-native';
|
||||
import FastImage from 'react-native-fast-image';
|
||||
import { IconButton } from '..';
|
||||
import { CheckBox, IconButton, MainButton, TextButton } from '..';
|
||||
import { UploadedMedia } from '../../models';
|
||||
import { addImage, deleteImage, getImages } from '../../providers/ecency/ecency';
|
||||
import Modal from '../modal';
|
||||
import styles from './uploadsGalleryModalStyles';
|
||||
import { proxifyImageSrc } from '@ecency/render-helper';
|
||||
import {View as AnimatedView} from 'react-native-animatable';
|
||||
|
||||
|
||||
export interface UploadsGalleryModalRef {
|
||||
@ -21,7 +22,7 @@ interface MediaInsertData {
|
||||
|
||||
interface UploadsGalleryModalProps {
|
||||
username:string;
|
||||
handleOnSelect:(data:MediaInsertData)=>void;
|
||||
handleOnSelect:(data:Array<MediaInsertData>)=>void;
|
||||
uploadedImage:MediaInsertData;
|
||||
}
|
||||
|
||||
@ -30,7 +31,8 @@ export const UploadsGalleryModal = forwardRef(({username, handleOnSelect, uploa
|
||||
|
||||
const [mediaUploads, setMediaUploads] = useState([]);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [showModal, setShowModal] = useState(false);
|
||||
const [showModal, setShowModal] = useState(false);
|
||||
const [indices, setIndices] = useState<Map<number, boolean>>(new Map());
|
||||
|
||||
|
||||
useImperativeHandle(ref, () => ({
|
||||
@ -44,6 +46,12 @@ export const UploadsGalleryModal = forwardRef(({username, handleOnSelect, uploa
|
||||
_getMediaUploads();
|
||||
}, []);
|
||||
|
||||
useEffect(()=>{
|
||||
if(!showModal){
|
||||
setIndices(new Map());
|
||||
}
|
||||
}, [showModal])
|
||||
|
||||
useEffect(() => {
|
||||
if(uploadedImage){
|
||||
_addUploadedImageToGallery();
|
||||
@ -67,11 +75,14 @@ export const UploadsGalleryModal = forwardRef(({username, handleOnSelect, uploa
|
||||
|
||||
|
||||
// remove image data from user's gallery
|
||||
const _deleteMediaItem = async (id:string) => {
|
||||
const _deleteMedia = async () => {
|
||||
try{
|
||||
setIsLoading(true);
|
||||
await deleteImage(id)
|
||||
for (const index of indices.keys()) {
|
||||
await deleteImage(mediaUploads[index]._id)
|
||||
}
|
||||
await _getMediaUploads();
|
||||
setIndices(new Map());
|
||||
setIsLoading(false);
|
||||
} catch(err){
|
||||
console.warn("failed to remove image from gallery", err)
|
||||
@ -92,45 +103,111 @@ export const UploadsGalleryModal = forwardRef(({username, handleOnSelect, uploa
|
||||
setIsLoading(false);
|
||||
}
|
||||
}catch(err){
|
||||
console.warn("Failed to get snippets")
|
||||
console.warn("Failed to get images")
|
||||
setIsLoading(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//render list item for snippet and handle actions;
|
||||
const _renderItem = ({ item }:{item:UploadedMedia, index:number}) => {
|
||||
|
||||
const _onPress = () => {
|
||||
//inserts media items in post body
|
||||
const _insertMedia = async (selectedIndex?:number) => {
|
||||
//TODO: debug why insert not working
|
||||
const map = selectedIndex ? new Map([[selectedIndex, true]]) : indices;
|
||||
|
||||
const data = {
|
||||
url:item.url,
|
||||
hash:item.url.split('/').pop()
|
||||
}
|
||||
const data = []
|
||||
for (const index of map.keys()) {
|
||||
console.log(index)
|
||||
const item = mediaUploads[index]
|
||||
|
||||
data.push({
|
||||
url:item.url,
|
||||
hash:item.url.split('/').pop()
|
||||
})
|
||||
|
||||
}
|
||||
handleOnSelect(data)
|
||||
setShowModal(false);
|
||||
}
|
||||
|
||||
const _onRemovePress = async () => {
|
||||
const _onConfirm = () => {
|
||||
_deleteMediaItem(item._id)
|
||||
//renders footer with add snipept button and shows new snippet modal
|
||||
const _renderFloatingPanel = () => {
|
||||
|
||||
if(!indices.size){
|
||||
return null
|
||||
}
|
||||
|
||||
const _onRemovePress = async () => {
|
||||
const _onConfirm = () => {
|
||||
_deleteMedia()
|
||||
}
|
||||
Alert.alert(
|
||||
intl.formatMessage({id:'alert.delete'}),
|
||||
intl.formatMessage({id:'alert.remove_alert'}),
|
||||
[{
|
||||
text:intl.formatMessage({id:'alert.cancel'}),
|
||||
style:'cancel'
|
||||
},{
|
||||
text:intl.formatMessage({id:'alert.confirm'}),
|
||||
onPress:_onConfirm
|
||||
}]
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
return (
|
||||
<AnimatedView animation={"slideInUp"} duration={300}>
|
||||
<View style={styles.floatingContainer}>
|
||||
<TextButton
|
||||
style={styles.cancelButton}
|
||||
onPress={_onRemovePress}
|
||||
text={intl.formatMessage({
|
||||
id: 'uploads_modal.btn_delete',
|
||||
})}
|
||||
/>
|
||||
<MainButton
|
||||
style={{ width: 136, marginLeft:12}}
|
||||
onPress={_insertMedia}
|
||||
iconName="plus"
|
||||
iconType="MaterialCommunityIcons"
|
||||
iconColor="white"
|
||||
text={intl.formatMessage({
|
||||
id: 'uploads_modal.btn_insert',
|
||||
})}
|
||||
/>
|
||||
</View>
|
||||
</AnimatedView>
|
||||
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
|
||||
//render list item for snippet and handle actions;
|
||||
const _renderItem = ({ item, index }:{item:UploadedMedia, index:number}) => {
|
||||
|
||||
const _onCheckPress = () => {
|
||||
//update selection indices
|
||||
if(indices.has(index)){
|
||||
indices.delete(index);
|
||||
}else {
|
||||
indices.set(index, true);
|
||||
}
|
||||
Alert.alert(
|
||||
intl.formatMessage({id:'alert.delete'}),
|
||||
intl.formatMessage({id:'alert.remove_alert'}),
|
||||
[{
|
||||
text:intl.formatMessage({id:'alert.cancel'}),
|
||||
style:'cancel'
|
||||
},{
|
||||
text:intl.formatMessage({id:'alert.confirm'}),
|
||||
onPress:_onConfirm
|
||||
}]
|
||||
)
|
||||
|
||||
|
||||
setIndices(new Map([...indices]));
|
||||
}
|
||||
|
||||
const _onPress = () => {
|
||||
|
||||
if(indices.size){
|
||||
_onCheckPress()
|
||||
}else {
|
||||
_insertMedia(index)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
const thumbUrl = proxifyImageSrc(item.url, 600, 500, Platform.OS === 'ios' ? 'match' : 'webp');
|
||||
|
||||
return (
|
||||
@ -139,14 +216,11 @@ export const UploadsGalleryModal = forwardRef(({username, handleOnSelect, uploa
|
||||
source={{uri:thumbUrl}}
|
||||
style={styles.mediaItem}
|
||||
/>
|
||||
<View style={styles.removeItemContainer}>
|
||||
<IconButton
|
||||
iconStyle={styles.itemIcon}
|
||||
style={styles.itemIconWrapper}
|
||||
iconType="MaterialCommunityIcons"
|
||||
name="delete"
|
||||
onPress={_onRemovePress}
|
||||
size={20}
|
||||
<View style={styles.checkContainer}>
|
||||
<CheckBox
|
||||
isChecked={indices.has(index)}
|
||||
clicked={_onCheckPress}
|
||||
style={styles.checkStyle}
|
||||
/>
|
||||
</View>
|
||||
|
||||
@ -176,6 +250,7 @@ export const UploadsGalleryModal = forwardRef(({username, handleOnSelect, uploa
|
||||
keyExtractor={(item) => `item_${item.url}`}
|
||||
renderItem={_renderItem}
|
||||
ListEmptyComponent={_renderEmptyContent}
|
||||
extraData={indices}
|
||||
numColumns={2}
|
||||
refreshControl={
|
||||
<RefreshControl
|
||||
@ -185,6 +260,7 @@ export const UploadsGalleryModal = forwardRef(({username, handleOnSelect, uploa
|
||||
}
|
||||
/>
|
||||
</View>
|
||||
{_renderFloatingPanel()}
|
||||
</View>
|
||||
)
|
||||
|
||||
|
@ -22,11 +22,17 @@ export default EStyleSheet.create({
|
||||
paddingHorizontal:16
|
||||
},
|
||||
floatingContainer:{
|
||||
flexDirection:'row',
|
||||
position:'absolute',
|
||||
bottom:0,
|
||||
right:20,
|
||||
right:0,
|
||||
left: 0,
|
||||
justifyContent:'flex-end',
|
||||
zIndex:10
|
||||
alignItems:'center',
|
||||
zIndex:10,
|
||||
paddingVertical:16,
|
||||
paddingHorizontal: 24,
|
||||
backgroundColor:'$primaryBackgroundColor'
|
||||
} as ViewStyle,
|
||||
|
||||
mediaItem:{
|
||||
@ -95,9 +101,14 @@ export default EStyleSheet.create({
|
||||
|
||||
} as ViewStyle,
|
||||
|
||||
removeItemContainer:{
|
||||
checkContainer:{
|
||||
position:'absolute',
|
||||
top:16,
|
||||
right:16
|
||||
} as ViewStyle
|
||||
})
|
||||
bottom:20,
|
||||
right:20
|
||||
} as ViewStyle,
|
||||
|
||||
checkStyle:{
|
||||
backgroundColor:'$white',
|
||||
} as ViewStyle,
|
||||
|
||||
})
|
||||
|
@ -363,6 +363,8 @@
|
||||
"title":"Uploaded Images",
|
||||
"title_remove_confirmation":"Delete image",
|
||||
"btn_add":"Image",
|
||||
"btn_insert":"INSERT",
|
||||
"btn_delete":"DELETE",
|
||||
"message_failed":"Failed to upload image"
|
||||
},
|
||||
"pincode": {
|
||||
|
Loading…
Reference in New Issue
Block a user