diff --git a/packages/candymachine/pinata-upload.ts b/packages/candymachine/pinata-upload.ts index 3b63118379..5c97ce635f 100644 --- a/packages/candymachine/pinata-upload.ts +++ b/packages/candymachine/pinata-upload.ts @@ -1,6 +1,6 @@ import axios from "axios"; -import { LocalFileData } from "../utils/types/feed"; +import { LocalFileData } from "../utils/types/files"; interface PinataFileProps { file: LocalFileData; diff --git a/packages/components/FilePreview/AudioView.tsx b/packages/components/FilePreview/AudioView.tsx index b101c3b02c..60fd6e6472 100644 --- a/packages/components/FilePreview/AudioView.tsx +++ b/packages/components/FilePreview/AudioView.tsx @@ -4,11 +4,11 @@ import { View, Image, TouchableOpacity } from "react-native"; import { ActivityIndicator } from "react-native-paper"; import { AudioWaveform } from "./AudioWaveform"; -import { ipfsURLToHTTPURL } from "./ipfs"; import pauseSVG from "../../../assets/icons/pause.svg"; import playSVG from "../../../assets/icons/play.svg"; import { useMaxResolution } from "../../hooks/useMaxResolution"; import { getAudioDuration } from "../../utils/audio"; +import { ipfsURLToHTTPURL } from "../../utils/ipfs"; import { errorColor, neutral00, @@ -17,7 +17,7 @@ import { } from "../../utils/style/colors"; import { fontSemibold13, fontSemibold14 } from "../../utils/style/fonts"; import { layout, screenContentMaxWidth } from "../../utils/style/layout"; -import { RemoteFileData } from "../../utils/types/feed"; +import { RemoteFileData } from "../../utils/types/files"; import { BrandText } from "../BrandText"; import { SVG } from "../SVG"; import { THUMBNAIL_WIDTH } from "../socialFeed/SocialThread/SocialMessageContent"; diff --git a/packages/components/FilePreview/EditableAudioPreview.tsx b/packages/components/FilePreview/EditableAudioPreview.tsx index 2ba285ae3c..20194d7461 100644 --- a/packages/components/FilePreview/EditableAudioPreview.tsx +++ b/packages/components/FilePreview/EditableAudioPreview.tsx @@ -15,7 +15,7 @@ import { } from "../../utils/style/colors"; import { fontMedium32, fontSemibold12 } from "../../utils/style/fonts"; import { layout } from "../../utils/style/layout"; -import { LocalFileData } from "../../utils/types/feed"; +import { LocalFileData } from "../../utils/types/files"; import { BrandText } from "../BrandText"; import { SVG } from "../SVG"; import { FileUploader } from "../fileUploader"; diff --git a/packages/components/FilePreview/FilesPreviewsContainer.tsx b/packages/components/FilePreview/FilesPreviewsContainer.tsx index 392af54e59..0aa172f034 100644 --- a/packages/components/FilePreview/FilesPreviewsContainer.tsx +++ b/packages/components/FilePreview/FilesPreviewsContainer.tsx @@ -8,7 +8,7 @@ import { VideoView } from "./VideoView"; import { GIF_MIME_TYPE } from "../../utils/mime"; import { convertGIFToLocalFileType } from "../../utils/social-feed"; import { layout } from "../../utils/style/layout"; -import { LocalFileData, RemoteFileData } from "../../utils/types/feed"; +import { LocalFileData, RemoteFileData } from "../../utils/types/files"; interface FilePreviewContainerProps { files?: LocalFileData[]; diff --git a/packages/components/FilePreview/ImagesFullViewModal.tsx b/packages/components/FilePreview/ImagesFullViewModal.tsx index 3314a058af..8e1efc94a4 100644 --- a/packages/components/FilePreview/ImagesFullViewModal.tsx +++ b/packages/components/FilePreview/ImagesFullViewModal.tsx @@ -7,9 +7,9 @@ import { } from "react-native"; import { SvgProps } from "react-native-svg"; -import { ipfsURLToHTTPURL } from "./ipfs"; import chevronLeft from "../../../assets/icons/chevron-left.svg"; import chevronRight from "../../../assets/icons/chevron-right.svg"; +import { ipfsURLToHTTPURL } from "../../utils/ipfs"; import { neutral22, neutral33 } from "../../utils/style/colors"; import { SVG } from "../SVG"; import ModalBase from "../modals/ModalBase"; diff --git a/packages/components/FilePreview/ImagesViews.tsx b/packages/components/FilePreview/ImagesViews.tsx index 34fa33f385..f28d648f17 100644 --- a/packages/components/FilePreview/ImagesViews.tsx +++ b/packages/components/FilePreview/ImagesViews.tsx @@ -3,11 +3,11 @@ import { Image, TouchableOpacity, View } from "react-native"; import { DeleteButton } from "./DeleteButton"; import { ImagesFullViewModal } from "./ImagesFullViewModal"; -import { ipfsURLToHTTPURL } from "./ipfs"; +import { ipfsURLToHTTPURL } from "../../utils/ipfs"; import { errorColor } from "../../utils/style/colors"; import { fontSemibold13 } from "../../utils/style/fonts"; import { layout } from "../../utils/style/layout"; -import { LocalFileData, RemoteFileData } from "../../utils/types/feed"; +import { LocalFileData, RemoteFileData } from "../../utils/types/files"; import { BrandText } from "../BrandText"; interface ImagePreviewProps { diff --git a/packages/components/FilePreview/VideoView.tsx b/packages/components/FilePreview/VideoView.tsx index b552186393..0b091b72de 100644 --- a/packages/components/FilePreview/VideoView.tsx +++ b/packages/components/FilePreview/VideoView.tsx @@ -3,11 +3,11 @@ import React from "react"; import { View } from "react-native"; import { DeleteButton } from "./DeleteButton"; -import { ipfsURLToHTTPURL } from "./ipfs"; +import { ipfsURLToHTTPURL } from "../../utils/ipfs"; import { errorColor } from "../../utils/style/colors"; import { fontSemibold13 } from "../../utils/style/fonts"; import { layout } from "../../utils/style/layout"; -import { LocalFileData, RemoteFileData } from "../../utils/types/feed"; +import { LocalFileData, RemoteFileData } from "../../utils/types/files"; import { BrandText } from "../BrandText"; interface VideoPreviewProps { diff --git a/packages/components/FilePreview/ipfs.ts b/packages/components/FilePreview/ipfs.ts deleted file mode 100644 index 8469975bd8..0000000000 --- a/packages/components/FilePreview/ipfs.ts +++ /dev/null @@ -1,15 +0,0 @@ -// temporary hotfix -// pinata-pinned files are weirdly handled by nft.storage gateway - -export const ipfsURLToHTTPURL = (ipfsURL: string | undefined) => { - if (!ipfsURL) { - return ""; - } - if (ipfsURL.startsWith("https://") || ipfsURL.startsWith("blob:")) { - return ipfsURL; - } - if (ipfsURL.startsWith("ipfs://")) { - return ipfsURL.replace("ipfs://", "https://cloudflare-ipfs.com/ipfs/"); - } - return "https://cloudflare-ipfs.com/ipfs/" + ipfsURL; -}; diff --git a/packages/components/fileUploader/FileUploader.type.ts b/packages/components/fileUploader/FileUploader.type.ts index d2a8e83dcd..6f028c0fef 100644 --- a/packages/components/fileUploader/FileUploader.type.ts +++ b/packages/components/fileUploader/FileUploader.type.ts @@ -1,7 +1,7 @@ import React from "react"; import { StyleProp, ViewStyle } from "react-native"; -import { LocalFileData } from "../../utils/types/feed"; +import { LocalFileData } from "../../utils/types/files"; export interface FileUploaderProps { onUpload: (files: LocalFileData[]) => void; diff --git a/packages/components/fileUploader/formatFile.ts b/packages/components/fileUploader/formatFile.ts index cc555761f6..cdddc3dd07 100644 --- a/packages/components/fileUploader/formatFile.ts +++ b/packages/components/fileUploader/formatFile.ts @@ -3,7 +3,7 @@ import { IMAGE_MIME_TYPES, VIDEO_MIME_TYPES, } from "./../../utils/mime"; -import { FileType, LocalFileData } from "../../utils/types/feed"; +import { FileType, LocalFileData } from "../../utils/types/files"; import { getAudioData } from "../../utils/waveform"; export const formatFile = async (file: File): Promise => { diff --git a/packages/components/socialFeed/NewsFeed/NewsFeed.type.ts b/packages/components/socialFeed/NewsFeed/NewsFeed.type.ts index 11e839ecd9..12d9baf14b 100644 --- a/packages/components/socialFeed/NewsFeed/NewsFeed.type.ts +++ b/packages/components/socialFeed/NewsFeed/NewsFeed.type.ts @@ -1,6 +1,6 @@ import { Post } from "../../../api/feed/v1/feed"; import { PostResult } from "../../../contracts-clients/teritori-social-feed/TeritoriSocialFeed.types"; -import { LocalFileData, RemoteFileData } from "../../../utils/types/feed"; +import { LocalFileData, RemoteFileData } from "../../../utils/types/files"; export enum PostCategory { Reaction, diff --git a/packages/components/socialFeed/NewsFeed/NewsFeedInput.tsx b/packages/components/socialFeed/NewsFeed/NewsFeedInput.tsx index a6b2a5f8b6..b987cee324 100644 --- a/packages/components/socialFeed/NewsFeed/NewsFeedInput.tsx +++ b/packages/components/socialFeed/NewsFeed/NewsFeedInput.tsx @@ -17,11 +17,7 @@ import { ReplyToType, SocialFeedMetadata, } from "./NewsFeed.type"; -import { - generatePostMetadata, - getPostCategory, - uploadPostFilesToPinata, -} from "./NewsFeedQueries"; +import { generatePostMetadata, getPostCategory } from "./NewsFeedQueries"; import { NotEnoughFundModal } from "./NotEnoughFundModal"; import audioSVG from "../../../../assets/icons/audio.svg"; import cameraSVG from "../../../../assets/icons/camera.svg"; @@ -43,6 +39,7 @@ import useSelectedWallet from "../../../hooks/useSelectedWallet"; import { getUserId, mustGetCosmosNetwork } from "../../../networks"; import { prettyPrice } from "../../../utils/coins"; import { defaultSocialFeedFee } from "../../../utils/fee"; +import { uploadFilesToPinata } from "../../../utils/ipfs"; import { AUDIO_MIME_TYPES, IMAGE_MIME_TYPES, @@ -77,7 +74,7 @@ import { SOCIAL_FEED_BREAKPOINT_M, } from "../../../utils/style/layout"; import { replaceBetweenString } from "../../../utils/text"; -import { LocalFileData, RemoteFileData } from "../../../utils/types/feed"; +import { LocalFileData, RemoteFileData } from "../../../utils/types/files"; import { BrandText } from "../../BrandText"; import { FilesPreviewsContainer } from "../../FilePreview/FilesPreviewsContainer"; import FlexRow from "../../FlexRow"; @@ -241,7 +238,7 @@ export const NewsFeedInput = React.forwardRef< if (formValues.files?.length) { const pinataJWTKey = await generateIpfsKey(selectedNetworkId, userId); if (pinataJWTKey) { - files = await uploadPostFilesToPinata({ + files = await uploadFilesToPinata({ files: formValues.files, pinataJWTKey, }); diff --git a/packages/components/socialFeed/NewsFeed/NewsFeedQueries.ts b/packages/components/socialFeed/NewsFeed/NewsFeedQueries.ts index a698cbe135..473e622467 100644 --- a/packages/components/socialFeed/NewsFeed/NewsFeedQueries.ts +++ b/packages/components/socialFeed/NewsFeed/NewsFeedQueries.ts @@ -1,5 +1,4 @@ import { coin } from "@cosmjs/amino"; -import { omit } from "lodash"; import { v4 as uuidv4 } from "uuid"; import { @@ -7,15 +6,14 @@ import { NewPostFormValues, SocialFeedMetadata, } from "./NewsFeed.type"; -import { pinataPinFileToIPFS } from "../../../candymachine/pinata-upload"; import { nonSigningSocialFeedClient, signingSocialFeedClient, } from "../../../client-creators/socialFeedClient"; import { Wallet } from "../../../context/WalletsProvider"; import { defaultSocialFeedFee } from "../../../utils/fee"; -import { ipfsURLToHTTPURL } from "../../../utils/ipfs"; -import { LocalFileData, RemoteFileData } from "../../../utils/types/feed"; +import { ipfsURLToHTTPURL, uploadFilesToPinata } from "../../../utils/ipfs"; +import { RemoteFileData } from "../../../utils/types/files"; interface GetAvailableFreePostParams { networkId: string; wallet?: Wallet; @@ -132,7 +130,7 @@ export const createPost = async ({ let files: RemoteFileData[] = []; if (formValues.files?.length && pinataJWTKey) { - files = await uploadPostFilesToPinata({ + files = await uploadFilesToPinata({ files: formValues.files, pinataJWTKey, }); @@ -177,50 +175,6 @@ export const createPost = async ({ return true; }; -interface UploadPostFilesToPinataParams { - files: LocalFileData[]; - pinataJWTKey: string; -} - -export const uploadPostFilesToPinata = async ({ - files, - pinataJWTKey, -}: UploadPostFilesToPinataParams): Promise => { - const storedFile = async (file: LocalFileData): Promise => { - const fileData = await pinataPinFileToIPFS({ - file, - pinataJWTKey, - }); - if (file.thumbnailFileData) { - const thumbnailData = await pinataPinFileToIPFS({ - file: file.thumbnailFileData, - pinataJWTKey, - }); - - return { - ...omit(file, "file"), - url: fileData?.IpfsHash || "", - thumbnailFileData: { - ...omit(file.thumbnailFileData, "file"), - url: thumbnailData?.IpfsHash || "", - }, - }; - } else { - return { - ...omit(file, "file"), - url: fileData?.IpfsHash || "", - }; - } - }; - - const queries = []; - for (const file of files) { - const storedFileQuery = storedFile(file); - queries.push(storedFileQuery); - } - return await Promise.all(queries); -}; - interface GeneratePostMetadataParams { title: string; message: string; diff --git a/packages/components/socialFeed/RichText/RichText.type.ts b/packages/components/socialFeed/RichText/RichText.type.ts index 9fd0949420..527b8ee727 100644 --- a/packages/components/socialFeed/RichText/RichText.type.ts +++ b/packages/components/socialFeed/RichText/RichText.type.ts @@ -1,6 +1,6 @@ import { EntityInstance } from "draft-js"; -import { LocalFileData, RemoteFileData } from "../../../utils/types/feed"; +import { LocalFileData, RemoteFileData } from "../../../utils/types/files"; export type PublishValues = { hashtags: string[]; diff --git a/packages/components/socialFeed/RichText/RichText.web.tsx b/packages/components/socialFeed/RichText/RichText.web.tsx index 63d5fb2d58..ac940817a5 100644 --- a/packages/components/socialFeed/RichText/RichText.web.tsx +++ b/packages/components/socialFeed/RichText/RichText.web.tsx @@ -81,7 +81,7 @@ import { import { neutral77 } from "../../../utils/style/colors"; import { fontSemibold14 } from "../../../utils/style/fonts"; import { layout, SOCIAL_FEED_BREAKPOINT_M } from "../../../utils/style/layout"; -import { LocalFileData } from "../../../utils/types/feed"; +import { LocalFileData } from "../../../utils/types/files"; import { BrandText } from "../../BrandText"; import { AudioView } from "../../FilePreview/AudioView"; import { EditableAudioPreview } from "../../FilePreview/EditableAudioPreview"; diff --git a/packages/components/socialFeed/SocialThread/ArticleRenderer.tsx b/packages/components/socialFeed/SocialThread/ArticleRenderer.tsx index ce37069805..5deee2f0df 100644 --- a/packages/components/socialFeed/SocialThread/ArticleRenderer.tsx +++ b/packages/components/socialFeed/SocialThread/ArticleRenderer.tsx @@ -1,11 +1,11 @@ import React from "react"; import { Image } from "react-native"; +import { ipfsURLToHTTPURL } from "../../../utils/ipfs"; import { ARTICLE_COVER_IMAGE_HEIGHT } from "../../../utils/social-feed"; import { layout } from "../../../utils/style/layout"; -import { RemoteFileData } from "../../../utils/types/feed"; +import { RemoteFileData } from "../../../utils/types/files"; import { BrandText } from "../../BrandText"; -import { ipfsURLToHTTPURL } from "../../FilePreview/ipfs"; import { SocialFeedMetadata } from "../NewsFeed/NewsFeed.type"; import { RichText } from "../RichText"; diff --git a/packages/utils/ipfs.ts b/packages/utils/ipfs.ts index 837241c733..8f3623184a 100644 --- a/packages/utils/ipfs.ts +++ b/packages/utils/ipfs.ts @@ -1,3 +1,86 @@ +import { omit } from "lodash"; + +import { mustGetFeedClient } from "./backend"; +import { LocalFileData, RemoteFileData } from "./types/files"; +import { pinataPinFileToIPFS } from "../candymachine/pinata-upload"; + +interface UploadPostFilesToPinataParams { + files: LocalFileData[]; + pinataJWTKey: string; +} + +export const uploadFilesToPinata = async ({ + files, + pinataJWTKey, +}: UploadPostFilesToPinataParams): Promise => { + const storedFile = async (file: LocalFileData): Promise => { + const fileData = await pinataPinFileToIPFS({ + file, + pinataJWTKey, + }); + if (file.thumbnailFileData) { + const thumbnailData = await pinataPinFileToIPFS({ + file: file.thumbnailFileData, + pinataJWTKey, + }); + + return { + ...omit(file, "file"), + url: fileData?.IpfsHash || "", + thumbnailFileData: { + ...omit(file.thumbnailFileData, "file"), + url: thumbnailData?.IpfsHash || "", + }, + }; + } else { + return { + ...omit(file, "file"), + url: fileData?.IpfsHash || "", + }; + } + }; + + const queries = []; + for (const file of files) { + const storedFileQuery = storedFile(file); + queries.push(storedFileQuery); + } + return await Promise.all(queries); +}; + +export const generateIpfsKey = async (networkId: string, userId: string) => { + try { + const backendClient = mustGetFeedClient(networkId); + const response = await backendClient.IPFSKey({ userId }); + return response.jwt; + } catch (e) { + console.error("ERROR WHILE GENERATING IPFSKey : ", e); + return undefined; + } +}; + +// Get IPFS Key and upload files. +// But you can do separately generateIpfsKey then uploadFilesToPinata (Ex in NewsFeedInput.tsx) +export const uploadFileToIPFS = async ( + file: LocalFileData, + networkId: string, + userId: string +) => { + let uploadedFiles: RemoteFileData[] = []; + const pinataJWTKey = await generateIpfsKey(networkId, userId); + + if (pinataJWTKey) { + uploadedFiles = await uploadFilesToPinata({ + files: [file], + pinataJWTKey, + }); + } + if (!uploadedFiles.find((file) => file.url)) { + console.error("upload file err : Fail to pin to IPFS"); + } else return uploadedFiles[0]; +}; + +// Used to get a correct image URL for displaying or storing export const ipfsURLToHTTPURL = (ipfsURL: string | undefined) => { if (!ipfsURL) { return ""; diff --git a/packages/utils/social-feed.ts b/packages/utils/social-feed.ts index 78b7c60b47..a1d315360b 100644 --- a/packages/utils/social-feed.ts +++ b/packages/utils/social-feed.ts @@ -1,7 +1,6 @@ import { mustGetFeedClient } from "./backend"; import { GIF_MIME_TYPE } from "./mime"; import { HASHTAG_REGEX, MENTION_REGEX, URL_REGEX } from "./regex"; -import { LocalFileData } from "./types/feed"; import { Post, Reaction } from "../api/feed/v1/feed"; import { PostCategory, @@ -10,6 +9,7 @@ import { } from "../components/socialFeed/NewsFeed/NewsFeed.type"; import { PostResult } from "../contracts-clients/teritori-social-feed/TeritoriSocialFeed.types"; import { getUserId } from "../networks"; +import {LocalFileData} from "./types/files"; export const DEFAULT_NAME = "Anon"; export const DEFAULT_USERNAME = "anonymous"; diff --git a/packages/utils/types/feed.ts b/packages/utils/types/files.ts similarity index 100% rename from packages/utils/types/feed.ts rename to packages/utils/types/files.ts diff --git a/packages/utils/waveform/waveform.web.ts b/packages/utils/waveform/waveform.web.ts index 696067f606..0d4e59fb2d 100644 --- a/packages/utils/waveform/waveform.web.ts +++ b/packages/utils/waveform/waveform.web.ts @@ -1,7 +1,7 @@ import WaveformData from "waveform-data"; import { BAR_LENGTH } from "./constants"; -import { AudioFileMetadata } from "../types/feed"; +import {AudioFileMetadata} from "../types/files"; //@ts-ignore window.AudioContext = window.AudioContext || window?.webkitAudioContext;