mirror of
https://github.com/ecency/ecency-mobile.git
synced 2024-12-22 12:51:42 +03:00
Merge branch 'development' into nt/comments-cache
This commit is contained in:
commit
2e47348c8f
@ -128,7 +128,7 @@
|
||||
"react-native-vector-icons": "^6.6.0",
|
||||
"react-native-version": "^4.0.0",
|
||||
"react-native-version-number": "^0.3.5",
|
||||
"react-native-webview": "^11.2.1",
|
||||
"react-native-webview": "^11.17.1",
|
||||
"react-native-youtube-iframe": "^2.1.1",
|
||||
"react-navigation": "^4.0.10",
|
||||
"react-navigation-drawer": "^2.3.3",
|
||||
|
@ -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,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
@ -7,6 +7,7 @@ import { AutoHeightImage } from '../autoHeightImage/autoHeightImage';
|
||||
import { useHtmlIframeProps, iframeModel } from '@native-html/iframe-plugin';
|
||||
import WebView from 'react-native-webview';
|
||||
import { VideoPlayer } from '..';
|
||||
import { Platform } from 'react-native';
|
||||
|
||||
interface PostHtmlRendererProps {
|
||||
contentWidth: number;
|
||||
@ -136,7 +137,10 @@ export const PostHtmlRenderer = memo(
|
||||
|
||||
//process video link
|
||||
if(tnode.classes?.indexOf('markdown-video-link') >= 0){
|
||||
if(isComment){
|
||||
|
||||
//TODO: remove android check when fix for react-native-weview scroll crash is available
|
||||
//ref: https://github.com/react-native-webview/react-native-webview/issues/2364
|
||||
if(isComment || Platform.OS === 'android'){
|
||||
const imgElement = tnode.children.find((child) => {
|
||||
return child.classes.indexOf('video-thumbnail') > 0 ? true : false;
|
||||
});
|
||||
@ -225,17 +229,30 @@ export const PostHtmlRenderer = memo(
|
||||
const checkSrcRegex = /(.*?)\.(mp4|webm|ogg)$/gi;
|
||||
const isVideoFormat = iframeProps.source.uri.match(checkSrcRegex);
|
||||
|
||||
//this hack help avoid autoplaying fullscreened iframe videos;
|
||||
const src = isVideoFormat ?
|
||||
{
|
||||
html: `
|
||||
<video width="100%" height="auto" controls>
|
||||
<source src="${iframeProps.source.uri}" type="video/mp4">
|
||||
</video>`,
|
||||
}:{
|
||||
uri: iframeProps.source.uri,
|
||||
};
|
||||
//this hack help avoid autoplaying fullscreened iframe videos;
|
||||
const src = isVideoFormat ?
|
||||
{
|
||||
html: `
|
||||
<video width="100%" height="auto" controls>
|
||||
<source src="${iframeProps.source.uri}" type="video/mp4">
|
||||
</video>`,
|
||||
}:{
|
||||
uri: iframeProps.source.uri,
|
||||
};
|
||||
|
||||
//TODO: remove android check logic when fix for react-native-webiew scrollview crash is available
|
||||
//ref: https://github.com/react-native-webview/react-native-webview/issues/2364
|
||||
if(isComment || Platform.OS === 'android'){
|
||||
const _onPress = () => {
|
||||
console.log('iframe thumb Pressed:', iframeProps);
|
||||
if (handleVideoPress) {
|
||||
handleVideoPress(iframeProps.source.uri);
|
||||
}
|
||||
};
|
||||
return (
|
||||
<VideoThumb contentWidth={contentWidth} onPress={_onPress} />
|
||||
)
|
||||
}else{
|
||||
return (
|
||||
<VideoPlayer
|
||||
mode='source'
|
||||
@ -243,6 +260,8 @@ export const PostHtmlRenderer = memo(
|
||||
contentWidth={contentWidth}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
return (
|
||||
|
@ -2,7 +2,7 @@ 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, MainButton, TextButton } from '..';
|
||||
import { UploadedMedia } from '../../models';
|
||||
import { addImage, deleteImage, getImages } from '../../providers/ecency/ecency';
|
||||
import Modal from '../modal';
|
||||
@ -21,7 +21,7 @@ interface MediaInsertData {
|
||||
|
||||
interface UploadsGalleryModalProps {
|
||||
username:string;
|
||||
handleOnSelect:(data:MediaInsertData)=>void;
|
||||
handleOnSelect:(data:Array<MediaInsertData>)=>void;
|
||||
uploadedImage:MediaInsertData;
|
||||
}
|
||||
|
||||
@ -31,6 +31,7 @@ export const UploadsGalleryModal = forwardRef(({username, handleOnSelect, uploa
|
||||
const [mediaUploads, setMediaUploads] = useState([]);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [showModal, setShowModal] = useState(false);
|
||||
const [indices, setIndices] = useState<Map<number, boolean>>(new Map());
|
||||
|
||||
|
||||
useImperativeHandle(ref, () => ({
|
||||
@ -44,6 +45,12 @@ export const UploadsGalleryModal = forwardRef(({username, handleOnSelect, uploa
|
||||
_getMediaUploads();
|
||||
}, []);
|
||||
|
||||
useEffect(()=>{
|
||||
if(!showModal){
|
||||
setIndices(new Map());
|
||||
}
|
||||
}, [showModal])
|
||||
|
||||
useEffect(() => {
|
||||
if(uploadedImage){
|
||||
_addUploadedImageToGallery();
|
||||
@ -67,11 +74,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 +102,107 @@ 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);
|
||||
}
|
||||
}
|
||||
|
||||
//inserts media items in post body
|
||||
const _insertMedia = async (selectedIndex?:number) => {
|
||||
const map = selectedIndex ? new Map([[selectedIndex, true]]) : indices;
|
||||
|
||||
const data = []
|
||||
for (const index of map.keys()) {
|
||||
console.log(index)
|
||||
const item = mediaUploads[index]
|
||||
|
||||
//render list item for snippet and handle actions;
|
||||
const _renderItem = ({ item }:{item:UploadedMedia, index:number}) => {
|
||||
data.push({
|
||||
url:item.url,
|
||||
hash:item.url.split('/').pop()
|
||||
})
|
||||
|
||||
const _onPress = () => {
|
||||
|
||||
const data = {
|
||||
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 (
|
||||
<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>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
|
||||
//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 +211,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 +245,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 +255,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:8,
|
||||
paddingHorizontal: 16,
|
||||
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": {
|
||||
|
@ -8856,10 +8856,10 @@ react-native-version@^4.0.0:
|
||||
resolve-from "^5.0.0"
|
||||
semver "^7.0.0"
|
||||
|
||||
react-native-webview@^11.2.1:
|
||||
version "11.2.3"
|
||||
resolved "https://registry.yarnpkg.com/react-native-webview/-/react-native-webview-11.2.3.tgz#1b82685ab60645d4161f2e25e98286493cdffa5d"
|
||||
integrity sha512-r/K+Lf/O5aij72gRndMX2qsyQ/WLtDPiO75SS57y6JjqSKxedGASVL9Jwl1TM7fCXqUq8dgiwik/LuBHbJXAEg==
|
||||
react-native-webview@^11.17.1:
|
||||
version "11.17.1"
|
||||
resolved "https://registry.yarnpkg.com/react-native-webview/-/react-native-webview-11.17.1.tgz#a7c9d995d749539995a4fdad8aa6456bac77fc8f"
|
||||
integrity sha512-gGdBavATj8Mya2VYZtWtB9cgOAgVJJGlgL5mo/EO8quBeI5L3IBy2ZQolsCyRRGFTUPCc3Ah0OwJal0PjijGqw==
|
||||
dependencies:
|
||||
escape-string-regexp "2.0.0"
|
||||
invariant "2.2.4"
|
||||
|
Loading…
Reference in New Issue
Block a user