mirror of
https://github.com/Qortal/q-support.git
synced 2025-02-11 17:55:50 +00:00
Merge pull request #11 from QortalSeth/main
Publishing is now a file instead of Base64. This will reduce load times by about 1/3 on publishes after this update.
This commit is contained in:
commit
9434eef241
@ -32,7 +32,7 @@ import {
|
||||
import { setNotification } from "../../state/features/notificationsSlice.ts";
|
||||
import { RootState } from "../../state/store.ts";
|
||||
import { BountyData, validateBountyInput } from "../../utils/qortalRequests.ts";
|
||||
import { objectToBase64 } from "../../utils/toBase64.js";
|
||||
import { objectToBase64, objectToFile } from "../../utils/PublishFormatter.ts";
|
||||
import { isNumber } from "../../utils/utilFunctions.ts";
|
||||
import {
|
||||
AutocompleteQappNames,
|
||||
@ -360,13 +360,12 @@ export const EditIssue = () => {
|
||||
if (log)
|
||||
console.log("% of characters used:", metadescription.length / 240);
|
||||
|
||||
const fileObjectToBase64 = await objectToBase64(issueObject);
|
||||
// Description is obtained from raw data
|
||||
const requestBodyJson: any = {
|
||||
action: "PUBLISH_QDN_RESOURCE",
|
||||
name: name,
|
||||
service: "DOCUMENT",
|
||||
data64: fileObjectToBase64,
|
||||
file: objectToFile(issueObject),
|
||||
title: title.slice(0, 50),
|
||||
description: metadescription,
|
||||
identifier: editIssueProperties.id,
|
||||
|
@ -27,7 +27,7 @@ import ShortUniqueId from "short-unique-id";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
|
||||
import { setNotification } from "../../state/features/notificationsSlice";
|
||||
import { objectToBase64 } from "../../utils/toBase64";
|
||||
import { objectToBase64, objectToFile } from "../../utils/PublishFormatter.ts";
|
||||
import { RootState } from "../../state/store";
|
||||
import {
|
||||
setEditPlaylist,
|
||||
@ -159,14 +159,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(() => {
|
||||
@ -266,13 +268,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 +311,10 @@ export const EditPlaylist = () => {
|
||||
};
|
||||
|
||||
const codes = videoStructured.map(item => `c:${item.code};`).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;
|
||||
@ -325,7 +326,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,
|
||||
|
@ -35,7 +35,7 @@ import { ThemeButtonBright } from "../../pages/Home/Home-styles.tsx";
|
||||
import { setNotification } from "../../state/features/notificationsSlice";
|
||||
import { RootState } from "../../state/store";
|
||||
import { BountyData, validateBountyInput } from "../../utils/qortalRequests.ts";
|
||||
import { objectToBase64 } from "../../utils/toBase64";
|
||||
import { objectToBase64, objectToFile } from "../../utils/PublishFormatter.ts";
|
||||
import { isNumber } from "../../utils/utilFunctions.ts";
|
||||
import {
|
||||
AutocompleteQappNames,
|
||||
@ -325,13 +325,12 @@ export const PublishIssue = ({ editId, editContent }: NewCrowdfundProps) => {
|
||||
if (log)
|
||||
console.log("% of characters used:", metadescription.length / 240);
|
||||
|
||||
const fileObjectToBase64 = await objectToBase64(issueObject);
|
||||
// Description is obtained from raw data
|
||||
const requestBodyJson: any = {
|
||||
action: "PUBLISH_QDN_RESOURCE",
|
||||
name: name,
|
||||
service: "DOCUMENT",
|
||||
data64: fileObjectToBase64,
|
||||
file: objectToFile(issueObject),
|
||||
title: title.slice(0, 50),
|
||||
description: metadescription,
|
||||
identifier: identifier + "_metadata",
|
||||
|
@ -239,7 +239,7 @@ const CommentCard = ({
|
||||
</StyledCardColComment>
|
||||
</StyledCardHeaderComment>
|
||||
<StyledCardContentComment>
|
||||
<StyledCardComment>{message}</StyledCardComment>
|
||||
<StyledCardComment paragraph={false}>{message}</StyledCardComment>
|
||||
</StyledCardContentComment>
|
||||
<Box
|
||||
sx={{
|
||||
|
@ -4,6 +4,7 @@ import { RootState } from "../../../state/store";
|
||||
import ShortUniqueId from "short-unique-id";
|
||||
import { setNotification } from "../../../state/features/notificationsSlice";
|
||||
import localforage from "localforage";
|
||||
import { stringToFile } from "../../../utils/PublishFormatter.ts";
|
||||
import {
|
||||
CommentInput,
|
||||
CommentInputContainer,
|
||||
@ -33,11 +34,13 @@ export interface Item {
|
||||
|
||||
export async function addItem(item: Item): Promise<void> {
|
||||
// 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
|
||||
@ -58,10 +61,10 @@ export async function addItem(item: Item): Promise<void> {
|
||||
}
|
||||
export async function updateItemDate(item: any): Promise<void> {
|
||||
// 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) {
|
||||
@ -124,11 +127,9 @@ 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 || "";
|
||||
|
||||
const notificationMessage = `This is an automated Q-Support notification indicating that someone has commented on your issue here:
|
||||
qortal://APP/Q-Support/issue/${postName}/${postId}`;
|
||||
@ -156,12 +157,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,
|
||||
});
|
||||
|
||||
@ -180,13 +180,6 @@ export const CommentEditor = ({
|
||||
});
|
||||
}
|
||||
if (!isReply && !isEdit) {
|
||||
// const notificationMessage = `This is an automated Q-Support notification indicating that someone has commented on your issue here:
|
||||
// qortal://APP/Q-Support/issue/${postName}/${postId}
|
||||
//
|
||||
// Here are the first ${maxNotificationLength} characters of the comment:
|
||||
//
|
||||
// ${value.substring(0, maxNotificationLength)}`;
|
||||
|
||||
await sendQchatDM(postName, notificationMessage);
|
||||
}
|
||||
return resourceResponse;
|
||||
@ -269,5 +262,3 @@ export const CommentEditor = ({
|
||||
</CommentInputContainer>
|
||||
);
|
||||
};
|
||||
|
||||
const sendDMwithComment = () => {};
|
||||
|
@ -93,8 +93,9 @@ export const StyledCardComment = styled(Typography)(({ theme }) => ({
|
||||
letterSpacing: 0,
|
||||
fontWeight: 400,
|
||||
color: theme.palette.text.primary,
|
||||
fontSize: "19px",
|
||||
fontSize: "100%",
|
||||
wordBreak: "break-word",
|
||||
whiteSpace: "pre-wrap",
|
||||
}));
|
||||
|
||||
export const TitleText = styled(Typography)({
|
||||
|
@ -1,6 +1,9 @@
|
||||
import { setFeeData } from "../../../state/features/globalSlice.ts";
|
||||
import { store } from "../../../state/store.js";
|
||||
import { objectToBase64 } from "../../../utils/toBase64.ts";
|
||||
import {
|
||||
objectToBase64,
|
||||
objectToFile,
|
||||
} from "../../../utils/PublishFormatter.ts";
|
||||
import { useTestIdentifiers } from "../../Identifiers.ts";
|
||||
import { appName, FEE_BASE, feeAmountBase, FeeType } from "../FeeData.tsx";
|
||||
|
||||
@ -50,7 +53,7 @@ export const addFeePrice = async (
|
||||
feeType: FeeType = "default",
|
||||
coinType: CoinType = "QORT"
|
||||
) => {
|
||||
let fees = await fetchFees();
|
||||
const fees = await fetchFees();
|
||||
|
||||
fees.push({
|
||||
time: Date.now(),
|
||||
@ -59,14 +62,13 @@ export const addFeePrice = async (
|
||||
coinType,
|
||||
});
|
||||
|
||||
const feesBase64 = await objectToBase64(fees);
|
||||
console.log("fees are: ", fees);
|
||||
await qortalRequest({
|
||||
action: "PUBLISH_QDN_RESOURCE",
|
||||
name: appName,
|
||||
identifier: FEE_BASE,
|
||||
service: feesPublishService,
|
||||
data64: feesBase64,
|
||||
file: objectToFile(fees),
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -1,3 +1,4 @@
|
||||
/* eslint-disable */
|
||||
import { configureStore } from "@reduxjs/toolkit";
|
||||
import authReducer from "./features/authSlice.js";
|
||||
import fileReducer from "./features/fileSlice.ts";
|
||||
|
@ -1,90 +1,107 @@
|
||||
export const toBase64 = (file: File): Promise<string | ArrayBuffer | null> =>
|
||||
export const publishFormatter = (
|
||||
file: File
|
||||
): Promise<string | ArrayBuffer | null> =>
|
||||
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)
|
||||
}
|
||||
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)
|
||||
}
|
||||
})
|
||||
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<string>((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 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 fileType = { type: "application/json" };
|
||||
// Step 2: Create a Blob from the JSON string
|
||||
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 = ''
|
||||
@ -99,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<Uint8Array> {
|
||||
return new Promise(
|
||||
(resolve: (value: Uint8Array) => void, reject: (reason?: any) => void) => {
|
||||
const reader = new FileReader()
|
||||
const reader = new FileReader();
|
||||
|
||||
reader.onload = function (event: ProgressEvent<FileReader>) {
|
||||
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<FileReader>) {
|
||||
reject(error)
|
||||
}
|
||||
reject(error);
|
||||
};
|
||||
|
||||
reader.readAsArrayBuffer(file)
|
||||
reader.readAsArrayBuffer(file);
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// export async function processFileInChunks(file: File, chunkSize = 1024 * 1024): Promise<Uint8Array> {
|
Loading…
x
Reference in New Issue
Block a user