rendering video status

This commit is contained in:
Nouman Tahir 2023-11-15 17:20:29 +05:00
parent 09be4dec9a
commit ccd7d99884
6 changed files with 210 additions and 65 deletions

View File

@ -0,0 +1,84 @@
import React from 'react';
import { proxifyImageSrc } from '@ecency/render-helper';
import {
Platform,
Text,
TouchableOpacity,
View
} from 'react-native';
import EStyleSheet from 'react-native-extended-stylesheet';
import FastImage from 'react-native-fast-image';
import {
default as AnimatedView,
ZoomIn
} from 'react-native-reanimated';
import { Icon } from '../..';
import styles from './uploadsGalleryModalStyles';
import { MediaItem } from '../../../providers/ecency/ecency.types';
import { useIntl } from 'react-intl';
interface Props {
item: MediaItem;
insertedMediaUrls: string[],
isDeleteMode: boolean;
isDeleting: boolean;
deleteIds: string[];
isExpandedMode: boolean;
onPress: () => void;
}
export const MediaPreviewItem = ({ item, insertedMediaUrls, isDeleteMode, isDeleting, deleteIds, isExpandedMode, onPress }: Props) => {
const intl = useIntl();
const thumbUrl =
item.thumbUrl ||
proxifyImageSrc(item.url, 600, 500, Platform.OS === 'ios' ? 'match' : 'webp');
let isInsertedTimes = 0;
insertedMediaUrls?.forEach((url) => (isInsertedTimes += url === item.url ? 1 : 0));
const isToBeDeleted = deleteIds.indexOf(item._id) >= 0;
const transformStyle = {
transform: isToBeDeleted ? [{ scaleX: 0.7 }, { scaleY: 0.7 }] : [],
};
const _renderStatus = () => (item.speakData &&
<View style={styles.statusContainer}>
<Text style={styles.statusText}>{intl.formatMessage({id:`uploads_modal.${item.speakData?.status}`})}</Text>
</View>
)
const _renderMinus = () =>
isDeleteMode && (
<AnimatedView.View entering={ZoomIn} style={styles.minusContainer}>
<Icon
color={EStyleSheet.value('$pureWhite')}
iconType="MaterialCommunityIcons"
name="minus"
size={20}
/>
</AnimatedView.View>
);
const _renderCounter = () =>
isInsertedTimes > 0 &&
!isDeleteMode && (
<AnimatedView.View entering={ZoomIn} style={styles.counterContainer}>
<Text style={styles.counterText}>{isInsertedTimes}</Text>
</AnimatedView.View>
);
return (
<TouchableOpacity onPress={onPress} disabled={isDeleting}>
<View style={transformStyle}>
<FastImage
source={{ uri: thumbUrl }}
style={isExpandedMode ? styles.gridMediaItem : styles.mediaItem}
/>
{_renderCounter()}
{_renderMinus()}
{_renderStatus()}
</View>
</TouchableOpacity>
);
}

View File

@ -28,6 +28,7 @@ import styles, {
} from './uploadsGalleryModalStyles';
import { editorQueries } from '../../../providers/queries';
import { MediaItem } from '../../../providers/ecency/ecency.types';
import { MediaPreviewItem } from './mediaPreviewItem';
type Props = {
insertedMediaUrls: string[];
@ -102,6 +103,7 @@ const UploadsGalleryContent = ({
// render list item for snippet and handle actions;
const _renderItem = ({ item, index }: { item: MediaItem; index: number }) => {
const _onPress = () => {
if (isDeleteMode) {
const idIndex = deleteIds.indexOf(item._id);
@ -116,48 +118,14 @@ const UploadsGalleryContent = ({
}
};
const thumbUrl =
item.thumbUrl ||
proxifyImageSrc(item.url, 600, 500, Platform.OS === 'ios' ? 'match' : 'webp');
let isInsertedTimes = 0;
insertedMediaUrls?.forEach((url) => (isInsertedTimes += url === item.url ? 1 : 0));
const isToBeDeleted = deleteIds.indexOf(item._id) >= 0;
const transformStyle = {
transform: isToBeDeleted ? [{ scaleX: 0.7 }, { scaleY: 0.7 }] : [],
};
const _renderMinus = () =>
isDeleteMode && (
<AnimatedView.View entering={ZoomIn} style={styles.minusContainer}>
<Icon
color={EStyleSheet.value('$pureWhite')}
iconType="MaterialCommunityIcons"
name="minus"
size={20}
/>
</AnimatedView.View>
);
const _renderCounter = () =>
isInsertedTimes > 0 &&
!isDeleteMode && (
<AnimatedView.View entering={ZoomIn} style={styles.counterContainer}>
<Text style={styles.counterText}>{isInsertedTimes}</Text>
</AnimatedView.View>
);
return (
<TouchableOpacity onPress={_onPress} disabled={isDeleting}>
<View style={transformStyle}>
<FastImage
source={{ uri: thumbUrl }}
style={isExpandedMode ? styles.gridMediaItem : styles.mediaItem}
/>
{_renderCounter()}
{_renderMinus()}
</View>
</TouchableOpacity>
);
return <MediaPreviewItem
item={item}
insertedMediaUrls={insertedMediaUrls}
deleteIds={deleteIds}
isDeleteMode={isDeleteMode}
isDeleting={isDeleting}
isExpandedMode={isExpandedMode}
onPress={_onPress} />
};
const _renderSelectButton = (iconName: string, text: string, onPress: () => void) => {

View File

@ -205,6 +205,25 @@ export default EStyleSheet.create({
fontSize: 16,
} as TextStyle,
statusContainer: {
backgroundColor: '$primaryBlue',
position: 'absolute',
bottom: 0,
left: 8,
right: 0,
borderBottomLeftRadius: 16,
borderBottomRightRadius: 16,
padding: 2,
height: 20,
justifyContent: 'center',
alignItems:'center'
} as ViewStyle,
statusText: {
color: '$pureWhite',
fontSize: 14,
} as TextStyle,
checkStyle: {
backgroundColor: '$white',
} as ViewStyle,

View File

@ -560,7 +560,11 @@
"confirm_delete":"Are you sure you want to delete images from your uploads",
"message_failed":"Failed to upload image",
"delete_failed":"Failed to delete image",
"failed_count":"Failed to upload {failedCount} of {totalCount} selected image(s)"
"failed_count":"Failed to upload {failedCount} of {totalCount} selected image(s)",
"publish_manual":"Ready",
"published":"Published",
"encoding_ipfs":"Encoding",
"deleted":"Deleted"
},
"pincode": {
"enter_text": "Enter PIN to unlock",

View File

@ -1,13 +1,14 @@
import { useQuery } from "@tanstack/react-query";
import { QueryKey, useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import { useIntl } from "react-intl";
import { useAppDispatch, useAppSelector } from "../../../hooks";
import { toastNotification } from "../../../redux/actions/uiAction";
import { MediaItem } from "../../ecency/ecency.types";
import { getAllVideoStatuses } from "../../speak/speak";
import { getAllVideoStatuses, markAsPublished } from "../../speak/speak";
import QUERIES from "../queryKeys";
import { extract3SpeakIds } from "../../../utils/editor";
import { useRef } from "react";
import { ThreeSpeakStatus, ThreeSpeakVideo } from "../../speak/speak.types";
import bugsnapInstance from "../../../config/bugsnag";
/**
* fetches and caches speak video uploads
@ -16,38 +17,38 @@ import { ThreeSpeakStatus, ThreeSpeakVideo } from "../../speak/speak.types";
export const useVideoUploadsQuery = () => {
const intl = useIntl();
const dispatch = useAppDispatch();
const currentAccount = useAppSelector((state) => state.account.currentAccount);
const pinHash = useAppSelector((state) => state.application.pin);
const _fetchVideoUploads = async () => getAllVideoStatuses(currentAccount, pinHash);
// TOOD: filter cache data for post edits to only show already published videos
return useQuery<MediaItem[]>([QUERIES.MEDIA.GET_VIDEOS], _fetchVideoUploads, {
initialData: [],
onError: () => {
dispatch(toastNotification(intl.formatMessage({ id: 'alert.fail' })));
},
initialData: [],
onError: () => {
dispatch(toastNotification(intl.formatMessage({ id: 'alert.fail' })));
},
});
};
};
export const useSpeakContentBuilder = () => {
const videoUploads = useVideoUploadsQuery();
const videoPublishMetaRef = useRef<ThreeSpeakVideo|null>(null);
const videoPublishMetaRef = useRef<ThreeSpeakVideo | null>(null);
const build = (body:string) => {
const build = (body: string) => {
let _newBody = body;
const _ids = extract3SpeakIds({body});
const _ids = extract3SpeakIds({ body });
_ids.forEach((id) => {
const mediaItem:MediaItem|undefined = videoUploads.data.find((item) => item._id === id);
if(mediaItem){
const mediaItem: MediaItem | undefined = videoUploads.data.find((item) => item._id === id);
if (mediaItem) {
//check if video is unpublished, set unpublish video meta
if(!videoPublishMetaRef.current && mediaItem.speakData?.status === ThreeSpeakStatus.READY){
if (!videoPublishMetaRef.current && mediaItem.speakData?.status === ThreeSpeakStatus.READY) {
videoPublishMetaRef.current = mediaItem.speakData;
}
@ -55,7 +56,7 @@ export const useSpeakContentBuilder = () => {
const _toReplaceStr = `[3speak](${id})`;
const _replacement = `<center>[![](${mediaItem.thumbUrl})](${mediaItem.url})</center>`
_newBody = _newBody.replace(_toReplaceStr, _replacement)
}
})
@ -64,6 +65,73 @@ export const useSpeakContentBuilder = () => {
return {
build,
videoPublishMeta:videoPublishMetaRef.current
videoPublishMeta: videoPublishMetaRef.current
}
}
}
export const useSpeakMutations = () => {
const intl = useIntl();
const dispatch = useAppDispatch();
const queryClient = useQueryClient();
const currentAccount = useAppSelector((state) => state.account.currentAccount);
const pinCode = useAppSelector((state) => state.application.pin);
// id is options, if no id is provided program marks all notifications as read;
const _mutationFn = async (id: string) => {
try {
const response = await markAsPublished(currentAccount, pinCode, id);
console.log('Speak video marked as published', response);
return true;
} catch (err) {
bugsnapInstance.notify(err);
}
};
const _options: UseMutationOptions<number, unknown, string | undefined, void> = {
onMutate: async (videoId) => {
// TODO: find a way to optimise mutations by avoiding too many loops
console.log('on mutate data', videoId);
// update query data
const videosCache: MediaItem[] | undefined = queryClient.getQueryData([
QUERIES.MEDIA.GET_VIDEOS,
]);
console.log('query data', videosCache);
if(!videosCache){
return;
}
const _vidIndex = videosCache.findIndex((item) => item._id === videoId);
if(_vidIndex){
const spkData = videosCache[_vidIndex].speakData;
if(spkData){
spkData.status = ThreeSpeakStatus.PUBLISHED;
}
}
queryClient.setQueryData([QUERIES.MEDIA.GET_VIDEOS], videosCache)
},
onSuccess: async (status, _id) => {
console.log('on success data', status);
queryClient.invalidateQueries([QUERIES.MEDIA.GET_VIDEOS]);
},
onError: () => {
dispatch(toastNotification(intl.formatMessage({ id: 'alert.fail' })));
},
};
const markAsPublishedMutation = useMutation(_mutationFn, _options);
return {
markAsPublishedMutation
}
};

View File

@ -599,7 +599,7 @@ class EditorContainer extends Component<EditorContainerProps, any> {
pinCode,
userActivityMutation,
speakContentBuilder,
// isDefaultFooter,
speakMutations,
} = this.props;
const { rewardType, isPostSending, thumbUrl, draftId, shouldReblog } = this.state;
@ -703,7 +703,8 @@ class EditorContainer extends Component<EditorContainerProps, any> {
}
//TODO: mark unpublished video as published on 3speak if that is the case
//mark unpublished video as published on 3speak if that is the case
speakMutations.markAsPublishedMutation.mutate(speakContentBuilder.videoPublishMeta._id)
// post publish updates
dispatch(deleteDraftCacheEntry(DEFAULT_USER_DRAFT_ID + currentAccount.name));
@ -1259,6 +1260,7 @@ const mapStateToProps = (state) => ({
const mapQueriesToProps = () => ({
queryClient: useQueryClient(),
speakContentBuilder: speakQueries.useSpeakContentBuilder(),
speakMutations: speakQueries.useSpeakMutations(),
userActivityMutation: useUserActivityMutation(),
postCachePrimer: usePostsCachePrimer(),
});