From ccd7d9988418d7cca0dde8a3b8b79d4d5bd5bc5b Mon Sep 17 00:00:00 2001 From: Nouman Tahir Date: Wed, 15 Nov 2023 17:20:29 +0500 Subject: [PATCH] rendering video status --- .../children/mediaPreviewItem.tsx | 84 ++++++++++++++ .../children/uploadsGalleryContent.tsx | 52 ++------- .../children/uploadsGalleryModalStyles.ts | 19 +++ src/config/locales/en-US.json | 6 +- .../queries/editorQueries/speakQueries.ts | 108 ++++++++++++++---- .../editor/container/editorContainer.tsx | 6 +- 6 files changed, 210 insertions(+), 65 deletions(-) create mode 100644 src/components/uploadsGalleryModal/children/mediaPreviewItem.tsx diff --git a/src/components/uploadsGalleryModal/children/mediaPreviewItem.tsx b/src/components/uploadsGalleryModal/children/mediaPreviewItem.tsx new file mode 100644 index 000000000..072cecf01 --- /dev/null +++ b/src/components/uploadsGalleryModal/children/mediaPreviewItem.tsx @@ -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 && + + {intl.formatMessage({id:`uploads_modal.${item.speakData?.status}`})} + + ) + + const _renderMinus = () => + isDeleteMode && ( + + + + ); + + const _renderCounter = () => + isInsertedTimes > 0 && + !isDeleteMode && ( + + {isInsertedTimes} + + ); + + return ( + + + + {_renderCounter()} + {_renderMinus()} + {_renderStatus()} + + + ); +} \ No newline at end of file diff --git a/src/components/uploadsGalleryModal/children/uploadsGalleryContent.tsx b/src/components/uploadsGalleryModal/children/uploadsGalleryContent.tsx index 5f9ade7ea..09332a7f3 100644 --- a/src/components/uploadsGalleryModal/children/uploadsGalleryContent.tsx +++ b/src/components/uploadsGalleryModal/children/uploadsGalleryContent.tsx @@ -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 && ( - - - - ); - - const _renderCounter = () => - isInsertedTimes > 0 && - !isDeleteMode && ( - - {isInsertedTimes} - - ); - - return ( - - - - {_renderCounter()} - {_renderMinus()} - - - ); + return }; const _renderSelectButton = (iconName: string, text: string, onPress: () => void) => { diff --git a/src/components/uploadsGalleryModal/children/uploadsGalleryModalStyles.ts b/src/components/uploadsGalleryModal/children/uploadsGalleryModalStyles.ts index 95c043a53..813b93679 100644 --- a/src/components/uploadsGalleryModal/children/uploadsGalleryModalStyles.ts +++ b/src/components/uploadsGalleryModal/children/uploadsGalleryModalStyles.ts @@ -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, diff --git a/src/config/locales/en-US.json b/src/config/locales/en-US.json index f99f422c5..0b2028bf4 100644 --- a/src/config/locales/en-US.json +++ b/src/config/locales/en-US.json @@ -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", diff --git a/src/providers/queries/editorQueries/speakQueries.ts b/src/providers/queries/editorQueries/speakQueries.ts index efe0b163e..fa0a6d1ea 100644 --- a/src/providers/queries/editorQueries/speakQueries.ts +++ b/src/providers/queries/editorQueries/speakQueries.ts @@ -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([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(null); + const videoPublishMetaRef = useRef(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 = `
[![](${mediaItem.thumbUrl})](${mediaItem.url})
` _newBody = _newBody.replace(_toReplaceStr, _replacement) - + } }) @@ -64,6 +65,73 @@ export const useSpeakContentBuilder = () => { return { build, - videoPublishMeta:videoPublishMetaRef.current + videoPublishMeta: videoPublishMetaRef.current } -} \ No newline at end of file +} + + + +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 = { + 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 + } +}; \ No newline at end of file diff --git a/src/screens/editor/container/editorContainer.tsx b/src/screens/editor/container/editorContainer.tsx index 903b22969..f609809fa 100644 --- a/src/screens/editor/container/editorContainer.tsx +++ b/src/screens/editor/container/editorContainer.tsx @@ -599,7 +599,7 @@ class EditorContainer extends Component { pinCode, userActivityMutation, speakContentBuilder, - // isDefaultFooter, + speakMutations, } = this.props; const { rewardType, isPostSending, thumbUrl, draftId, shouldReblog } = this.state; @@ -703,7 +703,8 @@ class EditorContainer extends Component { } - //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(), });