diff --git a/src/lib/fetch.ts b/src/lib/fetch.ts index 9753296..530a400 100644 --- a/src/lib/fetch.ts +++ b/src/lib/fetch.ts @@ -4,8 +4,8 @@ import { GRAPHQL_ENDPOINT, } from './consts'; import { IS_DEBUG } from './env'; -import { ThreadsUserProfileResponse } from '../types/threads-api'; -import { mapUserProfile } from './map'; +import { ThreadsRepliesResponse, ThreadsUserProfileResponse, UserThreadsResponse } from '../types/threads-api'; +import { mapThreadsReplies, mapUserProfile, mapUserThreads } from './map'; const fetchBase = ({ documentId, variables }) => { return fetch(GRAPHQL_ENDPOINT, { @@ -67,10 +67,12 @@ export const fetchUserProfileThreads = async ({ } const variables = { userID: userId }; - return fetchBase({ + const data = (await fetchBase({ variables, documentId: ENDPOINTS_DOCUMENT_ID.USER_PROFILE_THREADS, - }); + })) as UserThreadsResponse; + + return mapUserThreads(data); }; export const fetchUserReplies = async ({ @@ -91,15 +93,17 @@ export const fetchUserReplies = async ({ }); }; -export const fetchThreadReplies = ({ threadId }) => { +export const fetchThreadReplies = async ({ threadId }) => { const variables = { postID: threadId }; - return fetchBase({ + const data = (await fetchBase({ variables, documentId: ENDPOINTS_DOCUMENT_ID.USER_PROFILE_THREADS_REPLIES, - }); + })) as ThreadsRepliesResponse; + + return mapThreadsReplies(data); }; -export const fetchPostReplies = ({ threadId }) => { +export const fetchPostReplies = async ({ threadId }) => { const variables = { postID: threadId }; return fetchBase({ variables, diff --git a/src/lib/map.ts b/src/lib/map.ts index 70837dc..199045d 100644 --- a/src/lib/map.ts +++ b/src/lib/map.ts @@ -1,9 +1,10 @@ -import { ThreadsUserProfileResponse} from '../types/threads-api' +import { ThreadsRepliesResponse, ThreadsUserProfileResponse, UserThreadsResponse } from '../types/threads-api' /* {"data":{"userData":{"user":{"is_private":false,"profile_pic_url":"https://scontent.cdninstagram.com/v/t51.2885-19/358174537_954616899107816_8099109910283809308_n.jpg?stp=dst-jpg_s150x150&_nc_ht=scontent.cdninstagram.com&_nc_cat=108&_nc_ohc=s5qTOIc_KREAX8qfpDD&edm=APs17CUBAAAA&ccb=7-5&oh=00_AfDaktW3vHUeFvaE14qoy7LmddGuAqWUh2uirC7ulm_TsQ&oe=64B34341&_nc_sid=10d13b","username":"midu.dev","hd_profile_pic_versions":[{"height":320,"url":"https://scontent.cdninstagram.com/v/t51.2885-19/358174537_954616899107816_8099109910283809308_n.jpg?stp=dst-jpg_s320x320&_nc_ht=scontent.cdninstagram.com&_nc_cat=108&_nc_ohc=s5qTOIc_KREAX8qfpDD&edm=APs17CUBAAAA&ccb=7-5&oh=00_AfBUgVik0k-VaqXmyuuJUp6bEmAyDHIkkB3ssbnHYwGg_A&oe=64B34341&_nc_sid=10d13b","width":320},{"height":640,"url":"https://scontent.cdninstagram.com/v/t51.2885-19/358174537_954616899107816_8099109910283809308_n.jpg?stp=dst-jpg_s640x640&_nc_ht=scontent.cdninstagram.com&_nc_cat=108&_nc_ohc=s5qTOIc_KREAX8qfpDD&edm=APs17CUBAAAA&ccb=7-5&oh=00_AfCG0VVjm58zezRMrUgG_HlTuOL0MlMMsUpGRDgn4CrMiA&oe=64B34341&_nc_sid=10d13b","width":640}],"is_verified":false,"biography":"👨‍💻 Ingeniero de Software + JavaScript\n⌨️ Aprende Programación conmigo\n🏆 Google Expert + GitHub Star\n🙌 Comparto recursos y tutoriales","biography_with_entities":null,"follower_count":34756,"profile_context_facepile_users":null,"bio_links":[{"url":"https://twitch.tv/midudev"}],"pk":"8242141302","full_name":"midudev • Programación y Desarrollo JavaScript","id":null}}},"extensions":{"is_final":true}} */ +// Organises the original response of the user profile export const mapUserProfile = (rawResponse: ThreadsUserProfileResponse) => { const userApiResponse = rawResponse?.data?.userData?.user if (!userApiResponse) return null @@ -26,4 +27,29 @@ export const mapUserProfile = (rawResponse: ThreadsUserProfileResponse) => { full_name, profile_pics } +} + +// Organises the original response of the user threads +export const mapUserThreads = (rawResponse: UserThreadsResponse) => { + if (!rawResponse.data) return null + + return rawResponse.data.mediaData.threads.map(({ id, thread_items }) => { + return { id, items: thread_items } + }) +} + +// Organises the original response of the thread replies +export const mapThreadsReplies = (rawResponse: ThreadsRepliesResponse) => { + if (!rawResponse.data) return null + + const containing_thread = { + id: rawResponse.data.data.containing_thread.id, + items: rawResponse.data.data.containing_thread.thread_items + } + + const reply_threads = rawResponse.data.data.reply_threads.map(({ id, thread_items }) => { + return { id, thread_items } + }) + + return { containing_thread, reply_threads } } \ No newline at end of file diff --git a/src/types/threads-api.ts b/src/types/threads-api.ts index 51b95cf..fb1991b 100644 --- a/src/types/threads-api.ts +++ b/src/types/threads-api.ts @@ -1,3 +1,5 @@ +// User profile types + export type ThreadsUserProfileResponse = { data: Data; extensions: Extensions; @@ -40,3 +42,136 @@ export type HDProfilePicVersion = { export type Extensions = { is_final: boolean; } + +// User threads types + +export type UserThreadsResponse = { + data: { mediaData: { threads: Thread[] } }; + extensions: Extensions; +} + +export type Thread = { + thread_items: ThreadItem[]; + id: string; +} + +export type ThreadItem = { + post: Post; + line_type: LineType; + view_replies_cta_string: null | string; + reply_facepile_users: ReplyFacepileUser[]; + should_show_replies_cta: boolean; + __typename: ThreadItemTypename; +} + +export enum ThreadItemTypename { + XDTThreadItem = "XDTThreadItem", +} + +export enum LineType { + Line = "line", + None = "none", +} + +export type Post = { + user: User; + image_versions2: PostImageVersions2; + original_width: number; + original_height: number; + video_versions: VideoVersion[]; + carousel_media: CarouselMedia[] | null; + carousel_media_count: number | null; + pk: string; + has_audio: boolean | null; + text_post_app_info: TextPostAppInfo; + caption: Caption; + taken_at: number; + like_count: number; + code: string; + media_overlay_info: null; + id: string; +} + +export type Caption = { + text: string; +} + +export type CarouselMedia = { + image_versions2: CarouselMediaImageVersions2; + video_versions: any[]; + has_audio: boolean | null; + original_height: number; + original_width: number; + pk: string; + id: string; +} + +export type CarouselMediaImageVersions2 = { + candidates: PurpleCandidate[]; +} + +export type PurpleCandidate = { + url: string; +} + +export type PostImageVersions2 = { + candidates: FluffyCandidate[]; +} + +export type FluffyCandidate = { + height: number; + url: string; + width: number; + __typename: CandidateTypename; +} + +export enum CandidateTypename { + XDTImageCandidate = "XDTImageCandidate", +} + +export type TextPostAppInfo = { + link_preview_attachment: string | null; + share_info: ShareInfo; + reply_to_author: string | null; + is_post_unavailable: boolean; +} + +export type ShareInfo = { + quoted_post: any; + reposted_post: any; +} + +export type UserShort = { + profile_pic_url: string; + username: string; + id: string | null; + is_verified: boolean; + pk: string; +} + +export type VideoVersion = { + type: number; + url: string; + __typename: VideoVersionTypename; +} + +export enum VideoVersionTypename { + XDTVideoVersion = "XDTVideoVersion", +} + +export type ReplyFacepileUser = { + __typename: ReplyFacepileUserTypename; + id: null; + profile_pic_url: string; +} + +export enum ReplyFacepileUserTypename { + XDTUserDict = "XDTUserDict", +} + +// Threads replies types + +export type ThreadsRepliesResponse = { + data: { data: { containing_thread: Thread; reply_threads: Thread[] } }; + extensions: Extensions; +} \ No newline at end of file