From 88cd24ae4dbf84ac17ad10687924a83edb6cff25 Mon Sep 17 00:00:00 2001 From: IrohDW Date: Tue, 2 Jul 2024 13:45:08 -0600 Subject: [PATCH] Deleted videos no longer appear on Home Page. SuperLikes and comments publish as file instead of Base64. Fixed bug causing the minimum Superlike to be 10 QORT when it is supposed to be 1 QORT. --- .../Publish/EditPlaylist/EditPlaylist.tsx | 21 ++- .../Publish/EditVideo/EditVideo.tsx | 23 ++- .../common/Comments/CommentEditor.tsx | 25 +-- .../common/ContentButtons/SuperLike.tsx | 75 ++------- .../common/SuperLikesList/CommentEditor.tsx | 39 +++-- .../VideoContent/VideoContent.tsx | 9 +- src/utils/PublishFormatter.ts | 147 ++++++++++-------- 7 files changed, 162 insertions(+), 177 deletions(-) diff --git a/src/components/Publish/EditPlaylist/EditPlaylist.tsx b/src/components/Publish/EditPlaylist/EditPlaylist.tsx index 58509f4..59e0de6 100644 --- a/src/components/Publish/EditPlaylist/EditPlaylist.tsx +++ b/src/components/Publish/EditPlaylist/EditPlaylist.tsx @@ -31,7 +31,11 @@ import AddBoxIcon from "@mui/icons-material/AddBox"; import { useDropzone } from "react-dropzone"; import { setNotification } from "../../../state/features/notificationsSlice.ts"; -import { objectToBase64, uint8ArrayToBase64 } from "../../../utils/PublishFormatter.ts"; +import { + objectToBase64, + objectToFile, + uint8ArrayToBase64, +} from "../../../utils/PublishFormatter.ts"; import { RootState } from "../../../state/store.ts"; import { upsertVideosBeginning, @@ -165,14 +169,16 @@ export const EditPlaylist = () => { const responseDataSearchVid = await response.json(); if (responseDataSearchVid?.length > 0) { - let resourceData2 = responseDataSearchVid[0]; + const resourceData2 = responseDataSearchVid[0]; videos.push(resourceData2); } } } combinedData.videos = videos; setPlaylistData(combinedData); - } catch (error) {} + } catch (error) { + console.log(error); + } }, []); useEffect(() => { @@ -263,13 +269,13 @@ export const EditPlaylist = () => { if (!descriptionVid) throw new Error("cannot find video code"); // Split the string by ';' - let parts = descriptionVid.split(";"); + const parts = descriptionVid.split(";"); // Initialize a variable to hold the code value let codeValue = ""; // Loop through the parts to find the one that starts with 'code:' - for (let part of parts) { + for (const part of parts) { if (part.startsWith("code:")) { codeValue = part.split(":")[1]; break; @@ -309,11 +315,10 @@ export const EditPlaylist = () => { .map(item => `c:${item.code};`) .slice(0, 10) .join(""); - let metadescription = + const metadescription = `**category:${category};subcategory:${subcategory};${codes}**` + stringDescription.slice(0, 120); - const crowdfundObjectToBase64 = await objectToBase64(playlistObject); // Description is obtained from raw data let identifier = editVideoProperties?.id; @@ -333,7 +338,7 @@ export const EditPlaylist = () => { action: "PUBLISH_QDN_RESOURCE", name: username, service: "PLAYLIST", - data64: crowdfundObjectToBase64, + file: objectToFile(playlistObject), title: title.slice(0, 50), description: metadescription, identifier: identifier, diff --git a/src/components/Publish/EditVideo/EditVideo.tsx b/src/components/Publish/EditVideo/EditVideo.tsx index 62c1d27..d06c894 100644 --- a/src/components/Publish/EditVideo/EditVideo.tsx +++ b/src/components/Publish/EditVideo/EditVideo.tsx @@ -35,7 +35,11 @@ import AddBoxIcon from "@mui/icons-material/AddBox"; import { useDropzone } from "react-dropzone"; import { setNotification } from "../../../state/features/notificationsSlice.ts"; -import { objectToBase64, objectToFile, uint8ArrayToBase64 } from "../../../utils/PublishFormatter.ts"; +import { + objectToBase64, + objectToFile, + uint8ArrayToBase64, +} from "../../../utils/PublishFormatter.ts"; import { RootState } from "../../../state/store.ts"; import { upsertVideosBeginning, @@ -53,7 +57,11 @@ import { extractTextFromHTML } from "../../common/TextEditor/utils.ts"; import { toBase64 } from "../PublishVideo/PublishVideo.tsx"; import { FrameExtractor } from "../../common/FrameExtractor/FrameExtractor.tsx"; import { QTUBE_VIDEO_BASE } from "../../../constants/Identifiers.ts"; -import { maxSize, titleFormatter, videoMaxSize } from "../../../constants/Misc.ts"; +import { + maxSize, + titleFormatter, + videoMaxSize, +} from "../../../constants/Misc.ts"; const uid = new ShortUniqueId(); const shortuid = new ShortUniqueId({ length: 5 }); @@ -291,13 +299,12 @@ export const EditVideo = () => { `**category:${category};subcategory:${subcategory};code:${editVideoProperties.code}**` + description.slice(0, 150); - const videoObjectToFile = objectToFile(videoObject); // Description is obtained from raw data const requestBodyJson: any = { action: "PUBLISH_QDN_RESOURCE", name: username, service: "DOCUMENT", - file: videoObjectToFile, + file: objectToFile(videoObject), title: title.slice(0, 50), description: metadescription, identifier: editVideoProperties.id, @@ -394,7 +401,9 @@ export const EditVideo = () => { compressedFile = file; resolve(); }, - error(error) {console.log(error)}, + error(error) { + console.log(error); + }, }); }); if (!compressedFile) continue; @@ -406,7 +415,9 @@ export const EditVideo = () => { } setImageExtracts(imagesExtracts); - } catch (error) {console.log(error)} + } catch (error) { + console.log(error); + } }; return ( diff --git a/src/components/common/Comments/CommentEditor.tsx b/src/components/common/Comments/CommentEditor.tsx index a7ea8ad..4926cae 100644 --- a/src/components/common/Comments/CommentEditor.tsx +++ b/src/components/common/Comments/CommentEditor.tsx @@ -4,7 +4,10 @@ import { useDispatch, useSelector } from "react-redux"; import { RootState } from "../../../state/store"; import ShortUniqueId from "short-unique-id"; import { setNotification } from "../../../state/features/notificationsSlice"; -import { publishFormatter } from "../../../utils/PublishFormatter.ts"; +import { + publishFormatter, + stringToFile, +} from "../../../utils/PublishFormatter.ts"; import localforage from "localforage"; import { CommentInput, @@ -30,11 +33,13 @@ export interface Item { export async function addItem(item: Item): Promise { // Get all items - let notificationComments: Item[] = + const notificationComments: Item[] = (await notification.getItem("comments")) || []; // Find the item with the same id, if it exists - let existingItemIndex = notificationComments.findIndex(i => i.id === item.id); + const existingItemIndex = notificationComments.findIndex( + i => i.id === item.id + ); if (existingItemIndex !== -1) { // If the item exists, update its date @@ -55,10 +60,10 @@ export async function addItem(item: Item): Promise { } export async function updateItemDate(item: any): Promise { // Get all items - let notificationComments: Item[] = + const notificationComments: Item[] = (await notification.getItem("comments")) || []; - let notificationCreatorComment: any = + const notificationCreatorComment: any = (await notification.getItem("post-comments")) || {}; const findPostId = notificationCreatorComment[item.postId]; if (findPostId) { @@ -121,13 +126,10 @@ export const CommentEditor = ({ identifier: string, idForNotification?: string ) => { - let address; - let name; + const address = user?.address; + const name = user?.name || ""; let errorMsg = ""; - address = user?.address; - name = user?.name || ""; - if (!address) { errorMsg = "Cannot post: your address isn't available"; } @@ -150,12 +152,11 @@ export const CommentEditor = ({ } try { - const base64 = utf8ToBase64(value); const resourceResponse = await qortalRequest({ action: "PUBLISH_QDN_RESOURCE", name: name, service: "BLOG_COMMENT", - data64: base64, + file: stringToFile(value), identifier: identifier, }); dispatch( diff --git a/src/components/common/ContentButtons/SuperLike.tsx b/src/components/common/ContentButtons/SuperLike.tsx index ab8b9c3..b519f4d 100644 --- a/src/components/common/ContentButtons/SuperLike.tsx +++ b/src/components/common/ContentButtons/SuperLike.tsx @@ -21,7 +21,10 @@ import { MultiplePublish } from "../../Publish/MultiplePublish/MultiplePublishAl import { useDispatch, useSelector } from "react-redux"; import { setNotification } from "../../../state/features/notificationsSlice.ts"; import ShortUniqueId from "short-unique-id"; -import { objectToBase64 } from "../../../utils/PublishFormatter.ts"; +import { + objectToBase64, + objectToFile, +} from "../../../utils/PublishFormatter.ts"; import { minPriceSuperlike } from "../../../constants/Misc.ts"; import { CommentInput } from "../Comments/Comments-styles.tsx"; import { @@ -55,9 +58,7 @@ export const SuperLike = ({ const [isOpen, setIsOpen] = useState(false); const [superlikeDonationAmount, setSuperlikeDonationAmount] = - useState(10); - const [qortalDevDonationAmount, setQortalDevDonationAmount] = - useState(0); + useState(minPriceSuperlike); const [currentBalance, setCurrentBalance] = useState(""); const [comment, setComment] = useState(""); @@ -82,15 +83,12 @@ export const SuperLike = ({ if (!name) throw new Error("Could not retrieve content creator's name"); const estimatedTransactionFees = 0.1; const donationExceedsBalance = - superlikeDonationAmount + - qortalDevDonationAmount + - estimatedTransactionFees >= - +currentBalance; + superlikeDonationAmount + estimatedTransactionFees >= +currentBalance; if (donationExceedsBalance) { throw new Error("Total donations exceeds current balance"); } - let resName = await qortalRequest({ + const resName = await qortalRequest({ action: "GET_NAME_DATA", name: name, }); @@ -106,10 +104,10 @@ export const SuperLike = ({ superlikeDonationAmount < minPriceSuperlike ) throw new Error( - `The amount needs to be at least ${minPriceSuperlike} QORT` + `The amount is ${superlikeDonationAmount}, but it needs to be at least ${minPriceSuperlike} QORT` ); - let listOfPublishes = []; + const listOfPublishes = []; const res = await qortalRequest({ action: "SEND_COIN", @@ -118,24 +116,7 @@ export const SuperLike = ({ amount: superlikeDonationAmount, }); - const devDonation = qortalDevDonationAmount > 0; - if (devDonation) { - const devFundName = "DevFund"; - - let devFundNameData = await qortalRequest({ - action: "GET_NAME_DATA", - name: devFundName, - }); - - const devFundAddress = devFundNameData.owner; - const resDevFund = await qortalRequest({ - action: "SEND_COIN", - coin: "QORT", - destinationAddress: devFundAddress, - amount: qortalDevDonationAmount, - }); - } - let metadescription = `**sig:${ + const metadescription = `**sig:${ res.signature };${FOR}:${name}_${FOR_SUPER_LIKE};nm:${name.slice( 0, @@ -148,7 +129,7 @@ export const SuperLike = ({ 39 )}_${id}`; - const superLikeToBase64 = await objectToBase64({ + const superLikeToFile = objectToFile({ comment, transactionReference: res.signature, notificationInformation: { @@ -166,7 +147,7 @@ export const SuperLike = ({ action: "PUBLISH_QDN_RESOURCE", name: username, service: "BLOG_COMMENT", - data64: superLikeToBase64, + file: superLikeToFile, title: "", description: metadescription, identifier: identifierSuperLike, @@ -187,9 +168,9 @@ export const SuperLike = ({ dispatch( setNotification({ msg: - error || error?.error || error?.message || + error || "Failed to publish Super Like", alertType: "error", }) @@ -339,36 +320,6 @@ export const SuperLike = ({ InputLabelProps={{ style: { fontSize: "18px" } }} onChange={e => setComment(e.target.value)} /> - - - Would you like to donate to Qortal Development? - - setQortalDevDonationAmount(+e)} - InputProps={{ - style: { fontSize: 30, width: textFieldWidth }, - startAdornment: ( - - {"Qort - - ), - }} - /> diff --git a/src/components/common/SuperLikesList/CommentEditor.tsx b/src/components/common/SuperLikesList/CommentEditor.tsx index ec3e033..8d256a4 100644 --- a/src/components/common/SuperLikesList/CommentEditor.tsx +++ b/src/components/common/SuperLikesList/CommentEditor.tsx @@ -4,7 +4,12 @@ import { useDispatch, useSelector } from "react-redux"; import { RootState } from "../../../state/store"; import ShortUniqueId from "short-unique-id"; import { setNotification } from "../../../state/features/notificationsSlice"; -import { objectToBase64, publishFormatter } from "../../../utils/PublishFormatter.ts"; +import { + objectToBase64, + objectToFile, + publishFormatter, + stringToFile, +} from "../../../utils/PublishFormatter.ts"; import localforage from "localforage"; import { CommentInput, @@ -30,11 +35,13 @@ export interface Item { export async function addItem(item: Item): Promise { // Get all items - let notificationComments: Item[] = + const notificationComments: Item[] = (await notification.getItem("comments")) || []; // Find the item with the same id, if it exists - let existingItemIndex = notificationComments.findIndex(i => i.id === item.id); + const existingItemIndex = notificationComments.findIndex( + i => i.id === item.id + ); if (existingItemIndex !== -1) { // If the item exists, update its date @@ -55,10 +62,10 @@ export async function addItem(item: Item): Promise { } export async function updateItemDate(item: any): Promise { // Get all items - let notificationComments: Item[] = + const notificationComments: Item[] = (await notification.getItem("comments")) || []; - let notificationCreatorComment: any = + const notificationCreatorComment: any = (await notification.getItem("post-comments")) || {}; const findPostId = notificationCreatorComment[item.postId]; if (findPostId) { @@ -127,13 +134,10 @@ export const CommentEditor = ({ identifier: string, idForNotification?: string ) => { - let address; - let name; + const address = user?.address; + const name = user?.name || ""; let errorMsg = ""; - address = user?.address; - name = user?.name || ""; - if (!address) { errorMsg = "Cannot post: your address isn't available"; } @@ -156,7 +160,7 @@ export const CommentEditor = ({ } try { - let data64 = null; + let dataFile = null; let description = ""; let tag1 = ""; let superObj = {}; @@ -177,17 +181,18 @@ export const CommentEditor = ({ notificationInformation: comment.notificationInformation, about: comment.about, }; - const superLikeToBase64 = await objectToBase64(superObj); - data64 = superLikeToBase64; + const superLikeToFile = await objectToFile(superObj); + dataFile = superLikeToFile; } - if (isSuperLike && !data64) throw new Error("unable to edit Super like"); + if (isSuperLike && !dataFile) + throw new Error("unable to edit Super like"); - const base64 = utf8ToBase64(value); + const stringFile = stringToFile(value); const resourceResponse = await qortalRequest({ action: "PUBLISH_QDN_RESOURCE", name: name, service: "BLOG_COMMENT", - data64: isSuperLike ? data64 : base64, + file: isSuperLike ? dataFile : stringFile, identifier: identifier, description, tag1, @@ -248,7 +253,7 @@ export const CommentEditor = ({ let identifier = `${COMMENT_BASE}${postId.slice(-12)}_base_${id}`; let idForNotification = identifier; - let service = "BLOG_COMMENT"; + const service = "BLOG_COMMENT"; if (isReply && commentId) { const removeBaseCommentId = commentId; removeBaseCommentId.replace("_base_", ""); diff --git a/src/pages/ContentPages/VideoContent/VideoContent.tsx b/src/pages/ContentPages/VideoContent/VideoContent.tsx index 451fbb9..a6032d9 100644 --- a/src/pages/ContentPages/VideoContent/VideoContent.tsx +++ b/src/pages/ContentPages/VideoContent/VideoContent.tsx @@ -65,10 +65,10 @@ import { LikeAndDislike } from "../../../components/common/ContentButtons/LikeAn export function isTimestampWithinRange(resTimestamp, resCreated) { // Calculate the absolute difference in milliseconds - var difference = Math.abs(resTimestamp - resCreated); + const difference = Math.abs(resTimestamp - resCreated); // 2 minutes in milliseconds - var twoMinutesInMilliseconds = 3 * 60 * 1000; + const twoMinutesInMilliseconds = 3 * 60 * 1000; // Check if the difference is within 2 minutes return difference <= twoMinutesInMilliseconds; @@ -282,6 +282,7 @@ export const VideoContent = () => { } } } catch (error) { + console.log(error); } finally { dispatch(setIsLoadingGlobal(false)); } @@ -356,7 +357,9 @@ export const VideoContent = () => { }, ]; } - } catch (error) {} + } catch (error) { + console.log(error); + } } } diff --git a/src/utils/PublishFormatter.ts b/src/utils/PublishFormatter.ts index 59dfe42..7954021 100644 --- a/src/utils/PublishFormatter.ts +++ b/src/utils/PublishFormatter.ts @@ -1,98 +1,107 @@ -export const publishFormatter = (file: File): Promise => +export const publishFormatter = ( + file: File +): Promise => new Promise((resolve, reject) => { - const reader = new FileReader() - reader.readAsDataURL(file) + const reader = new FileReader(); + reader.readAsDataURL(file); reader.onload = () => { - const result = reader.result - reader.onload = null // remove onload handler - reader.onerror = null // remove onerror handler - resolve(result) - } - - reader.onerror = (error) => { - reader.onload = null // remove onload handler - reader.onerror = null // remove onerror handler - reject(error) - } - }) + const result = reader.result; + reader.onload = null; // remove onload handler + reader.onerror = null; // remove onerror handler + resolve(result); + }; + + reader.onerror = error => { + reader.onload = null; // remove onload handler + reader.onerror = null; // remove onerror handler + reject(error); + }; + }); export function objectToBase64(obj: any) { // Step 1: Convert the object to a JSON string - const jsonString = JSON.stringify(obj) + const jsonString = JSON.stringify(obj); // Step 2: Create a Blob from the JSON string - const blob = new Blob([jsonString], { type: 'application/json' }) + const blob = new Blob([jsonString], { type: "application/json" }); // Step 3: Create a FileReader to read the Blob as a base64-encoded string return new Promise((resolve, reject) => { - const reader = new FileReader() + const reader = new FileReader(); reader.onloadend = () => { - if (typeof reader.result === 'string') { + if (typeof reader.result === "string") { // Remove 'data:application/json;base64,' prefix const base64 = reader.result.replace( - 'data:application/json;base64,', - '' - ) - resolve(base64) + "data:application/json;base64,", + "" + ); + resolve(base64); } else { - reject(new Error('Failed to read the Blob as a base64-encoded string')) + reject(new Error("Failed to read the Blob as a base64-encoded string")); } - } + }; reader.onerror = () => { - reject(reader.error) - } - reader.readAsDataURL(blob) - }) + reject(reader.error); + }; + reader.readAsDataURL(blob); + }); } -export function objectToFile(obj: any) { +export const stringToFile = (text: string) => { + return new File([text], "", { + type: "text/plain", + }); +}; +export const objectToFile = (obj: object) => { // Step 1: Convert the object to a JSON string - const jsonString = JSON.stringify(obj) + const jsonString = JSON.stringify(obj); + const fileType = { type: "application/json" }; // Step 2: Create a Blob from the JSON string - return new Blob([jsonString], { type: 'application/json' }) -} + const blob = new Blob([jsonString], fileType); + return new File([blob], ``, fileType); +}; export function objectToUint8Array(obj: any) { // Convert the object to a JSON string - const jsonString = JSON.stringify(obj) + const jsonString = JSON.stringify(obj); // Encode the JSON string as a byte array using TextEncoder - const encoder = new TextEncoder() - const byteArray = encoder.encode(jsonString) + const encoder = new TextEncoder(); + const byteArray = encoder.encode(jsonString); // Create a new Uint8Array and set its content to the encoded byte array - const uint8Array = new Uint8Array(byteArray) + const uint8Array = new Uint8Array(byteArray); - return uint8Array + return uint8Array; } export function uint8ArrayToBase64(uint8Array: Uint8Array): string { - const length = uint8Array.length - let binaryString = '' - const chunkSize = 1024 * 1024 // Process 1MB at a time + const length = uint8Array.length; + let binaryString = ""; + const chunkSize = 1024 * 1024; // Process 1MB at a time for (let i = 0; i < length; i += chunkSize) { - const chunkEnd = Math.min(i + chunkSize, length) - const chunk = uint8Array.subarray(i, chunkEnd) - binaryString += Array.from(chunk, (byte) => String.fromCharCode(byte)).join( - '' - ) + const chunkEnd = Math.min(i + chunkSize, length); + const chunk = uint8Array.subarray(i, chunkEnd); + binaryString += Array.from(chunk, byte => String.fromCharCode(byte)).join( + "" + ); } - return btoa(binaryString) + return btoa(binaryString); } export function objectToUint8ArrayFromResponse(obj: any) { - const len = Object.keys(obj).length - const result = new Uint8Array(len) + const len = Object.keys(obj).length; + const result = new Uint8Array(len); for (let i = 0; i < len; i++) { - result[i] = obj[i] + result[i] = obj[i]; } - return result + return result; } // export function uint8ArrayToBase64(arrayBuffer: Uint8Array): string { // let binary = '' @@ -107,46 +116,46 @@ export function objectToUint8ArrayFromResponse(obj: any) { // } export function base64ToUint8Array(base64: string) { - const binaryString = atob(base64) - const len = binaryString.length - const bytes = new Uint8Array(len) + const binaryString = atob(base64); + const len = binaryString.length; + const bytes = new Uint8Array(len); for (let i = 0; i < len; i++) { - bytes[i] = binaryString.charCodeAt(i) + bytes[i] = binaryString.charCodeAt(i); } - return bytes + return bytes; } export function uint8ArrayToObject(uint8Array: Uint8Array) { // Decode the byte array using TextDecoder - const decoder = new TextDecoder() - const jsonString = decoder.decode(uint8Array) + const decoder = new TextDecoder(); + const jsonString = decoder.decode(uint8Array); // Convert the JSON string back into an object - const obj = JSON.parse(jsonString) + const obj = JSON.parse(jsonString); - return obj + return obj; } export function processFileInChunks(file: File): Promise { return new Promise( (resolve: (value: Uint8Array) => void, reject: (reason?: any) => void) => { - const reader = new FileReader() + const reader = new FileReader(); reader.onload = function (event: ProgressEvent) { - const arrayBuffer = event.target?.result as ArrayBuffer - const uint8Array = new Uint8Array(arrayBuffer) - resolve(uint8Array) - } + const arrayBuffer = event.target?.result as ArrayBuffer; + const uint8Array = new Uint8Array(arrayBuffer); + resolve(uint8Array); + }; reader.onerror = function (error: ProgressEvent) { - reject(error) - } + reject(error); + }; - reader.readAsArrayBuffer(file) + reader.readAsArrayBuffer(file); } - ) + ); } // export async function processFileInChunks(file: File, chunkSize = 1024 * 1024): Promise {