Medium Get Long Feed

Get long feed for current tag

  1. // ==UserScript==
  2. // @name Medium Get Long Feed
  3. // @namespace http://tampermonkey.net/
  4. // @version 5.2.0
  5. // @description Get long feed for current tag
  6. // @author Thomas Theiner
  7. // @match https://medium.com/?tag=*
  8. // @match https://medium.com
  9. // @icon https://www.google.com/s2/favicons?sz=64&domain=medium.com
  10. // @license MIT
  11. // ==/UserScript==
  12.  
  13. async function makeOutputPossible(delay = 0) {
  14. return new Promise(resolve => {
  15. setTimeout(resolve, delay);
  16. });
  17. }
  18.  
  19. async function fetchFeed(nextPaging, tag) {
  20. let bodyData = [
  21. {
  22. "operationName": "WebInlineTopicFeedQuery",
  23. "variables": {
  24. "tagSlug": tag,
  25. "paging": nextPaging,
  26. "skipCache": false
  27. },
  28. "query": "query WebInlineTopicFeedQuery($tagSlug: String!, $paging: PagingOptions!, $skipCache: Boolean) {\n personalisedTagFeed(tagSlug: $tagSlug, paging: $paging, skipCache: $skipCache) {\n items {\n ... on TagFeedItem {\n feedId\n reason\n moduleSourceEncoding\n post {\n ...PostPreview_post\n __typename\n }\n __typename\n }\n __typename\n }\n pagingInfo {\n next {\n source\n limit\n from\n to\n __typename\n }\n __typename\n }\n __typename\n }\n}\n\nfragment PostPreview_post on Post {\n id\n creator {\n ...PostPreview_user\n __typename\n id\n }\n collection {\n ...CardByline_collection\n ...ExpandablePostByline_collection\n __typename\n id\n }\n ...InteractivePostBody_postPreview\n firstPublishedAt\n isLocked\n isSeries\n latestPublishedAt\n inResponseToCatalogResult {\n __typename\n }\n pinnedAt\n pinnedByCreatorAt\n previewImage {\n id\n focusPercentX\n focusPercentY\n __typename\n }\n readingTime\n sequence {\n slug\n __typename\n }\n title\n uniqueSlug\n ...CardByline_post\n ...PostFooterActionsBar_post\n ...InResponseToEntityPreview_post\n ...PostScrollTracker_post\n ...HighDensityPreview_post\n __typename\n}\n\nfragment PostPreview_user on User {\n __typename\n name\n username\n ...CardByline_user\n ...ExpandablePostByline_user\n id\n}\n\nfragment CardByline_user on User {\n __typename\n id\n name\n username\n mediumMemberAt\n socialStats {\n followerCount\n __typename\n }\n ...useIsVerifiedBookAuthor_user\n ...userUrl_user\n ...UserMentionTooltip_user\n}\n\nfragment useIsVerifiedBookAuthor_user on User {\n verifications {\n isBookAuthor\n __typename\n }\n __typename\n id\n}\n\nfragment userUrl_user on User {\n __typename\n id\n customDomainState {\n live {\n domain\n __typename\n }\n __typename\n }\n hasSubdomain\n username\n}\n\nfragment UserMentionTooltip_user on User {\n id\n name\n username\n bio\n imageId\n mediumMemberAt\n membership {\n tier\n __typename\n id\n }\n ...UserAvatar_user\n ...UserFollowButton_user\n ...useIsVerifiedBookAuthor_user\n __typename\n}\n\nfragment UserAvatar_user on User {\n __typename\n id\n imageId\n mediumMemberAt\n membership {\n tier\n __typename\n id\n }\n name\n username\n ...userUrl_user\n}\n\nfragment UserFollowButton_user on User {\n ...UserFollowButtonSignedIn_user\n ...UserFollowButtonSignedOut_user\n __typename\n id\n}\n\nfragment UserFollowButtonSignedIn_user on User {\n id\n name\n __typename\n}\n\nfragment UserFollowButtonSignedOut_user on User {\n id\n ...SusiClickable_user\n __typename\n}\n\nfragment SusiClickable_user on User {\n ...SusiContainer_user\n __typename\n id\n}\n\nfragment SusiContainer_user on User {\n ...SignInOptions_user\n ...SignUpOptions_user\n __typename\n id\n}\n\nfragment SignInOptions_user on User {\n id\n name\n __typename\n}\n\nfragment SignUpOptions_user on User {\n id\n name\n __typename\n}\n\nfragment ExpandablePostByline_user on User {\n __typename\n id\n name\n imageId\n ...userUrl_user\n ...useIsVerifiedBookAuthor_user\n}\n\nfragment CardByline_collection on Collection {\n name\n ...collectionUrl_collection\n __typename\n id\n}\n\nfragment collectionUrl_collection on Collection {\n id\n domain\n slug\n __typename\n}\n\nfragment ExpandablePostByline_collection on Collection {\n __typename\n id\n name\n domain\n slug\n}\n\nfragment InteractivePostBody_postPreview on Post {\n extendedPreviewContent(\n truncationConfig: {previewParagraphsWordCountThreshold: 400, minimumWordLengthForTruncation: 150, truncateAtEndOfSentence: true, showFullImageCaptions: true, shortformPreviewParagraphsWordCountThreshold: 30, shortformMinimumWordLengthForTruncation: 30}\n ) {\n bodyModel {\n ...PostBody_bodyModel\n __typename\n }\n isFullContent\n __typename\n }\n __typename\n id\n}\n\nfragment PostBody_bodyModel on RichText {\n sections {\n name\n startIndex\n textLayout\n imageLayout\n backgroundImage {\n id\n originalHeight\n originalWidth\n __typename\n }\n videoLayout\n backgroundVideo {\n videoId\n originalHeight\n originalWidth\n previewImageId\n __typename\n }\n __typename\n }\n paragraphs {\n id\n ...PostBodySection_paragraph\n __typename\n }\n ...normalizedBodyModel_richText\n __typename\n}\n\nfragment PostBodySection_paragraph on Paragraph {\n name\n ...PostBodyParagraph_paragraph\n __typename\n id\n}\n\nfragment PostBodyParagraph_paragraph on Paragraph {\n name\n type\n ...ImageParagraph_paragraph\n ...TextParagraph_paragraph\n ...IframeParagraph_paragraph\n ...MixtapeParagraph_paragraph\n ...CodeBlockParagraph_paragraph\n __typename\n id\n}\n\nfragment ImageParagraph_paragraph on Paragraph {\n href\n layout\n metadata {\n id\n originalHeight\n originalWidth\n focusPercentX\n focusPercentY\n alt\n __typename\n }\n ...Markups_paragraph\n ...ParagraphRefsMapContext_paragraph\n ...PostAnnotationsMarker_paragraph\n __typename\n id\n}\n\nfragment Markups_paragraph on Paragraph {\n name\n text\n hasDropCap\n dropCapImage {\n ...MarkupNode_data_dropCapImage\n __typename\n id\n }\n markups {\n ...Markups_markup\n __typename\n }\n __typename\n id\n}\n\nfragment MarkupNode_data_dropCapImage on ImageMetadata {\n ...DropCap_image\n __typename\n id\n}\n\nfragment DropCap_image on ImageMetadata {\n id\n originalHeight\n originalWidth\n __typename\n}\n\nfragment Markups_markup on Markup {\n type\n start\n end\n href\n anchorType\n userId\n linkMetadata {\n httpStatus\n __typename\n }\n __typename\n}\n\nfragment ParagraphRefsMapContext_paragraph on Paragraph {\n id\n name\n text\n __typename\n}\n\nfragment PostAnnotationsMarker_paragraph on Paragraph {\n ...PostViewNoteCard_paragraph\n __typename\n id\n}\n\nfragment PostViewNoteCard_paragraph on Paragraph {\n name\n __typename\n id\n}\n\nfragment TextParagraph_paragraph on Paragraph {\n type\n hasDropCap\n codeBlockMetadata {\n mode\n lang\n __typename\n }\n ...Markups_paragraph\n ...ParagraphRefsMapContext_paragraph\n __typename\n id\n}\n\nfragment IframeParagraph_paragraph on Paragraph {\n type\n iframe {\n mediaResource {\n id\n iframeSrc\n iframeHeight\n iframeWidth\n title\n __typename\n }\n __typename\n }\n layout\n ...Markups_paragraph\n __typename\n id\n}\n\nfragment MixtapeParagraph_paragraph on Paragraph {\n type\n mixtapeMetadata {\n href\n mediaResource {\n mediumCatalog {\n id\n __typename\n }\n __typename\n }\n __typename\n }\n ...GenericMixtapeParagraph_paragraph\n __typename\n id\n}\n\nfragment GenericMixtapeParagraph_paragraph on Paragraph {\n text\n mixtapeMetadata {\n href\n thumbnailImageId\n __typename\n }\n markups {\n start\n end\n type\n href\n __typename\n }\n __typename\n id\n}\n\nfragment CodeBlockParagraph_paragraph on Paragraph {\n codeBlockMetadata {\n lang\n mode\n __typename\n }\n __typename\n id\n}\n\nfragment normalizedBodyModel_richText on RichText {\n paragraphs {\n ...normalizedBodyModel_richText_paragraphs\n __typename\n }\n sections {\n startIndex\n ...getSectionEndIndex_section\n __typename\n }\n ...getParagraphStyles_richText\n ...getParagraphSpaces_richText\n __typename\n}\n\nfragment normalizedBodyModel_richText_paragraphs on Paragraph {\n markups {\n ...normalizedBodyModel_richText_paragraphs_markups\n __typename\n }\n codeBlockMetadata {\n lang\n mode\n __typename\n }\n ...getParagraphHighlights_paragraph\n ...getParagraphPrivateNotes_paragraph\n __typename\n id\n}\n\nfragment normalizedBodyModel_richText_paragraphs_markups on Markup {\n type\n __typename\n}\n\nfragment getParagraphHighlights_paragraph on Paragraph {\n name\n __typename\n id\n}\n\nfragment getParagraphPrivateNotes_paragraph on Paragraph {\n name\n __typename\n id\n}\n\nfragment getSectionEndIndex_section on Section {\n startIndex\n __typename\n}\n\nfragment getParagraphStyles_richText on RichText {\n paragraphs {\n text\n type\n __typename\n }\n sections {\n ...getSectionEndIndex_section\n __typename\n }\n __typename\n}\n\nfragment getParagraphSpaces_richText on RichText {\n paragraphs {\n layout\n metadata {\n originalHeight\n originalWidth\n id\n __typename\n }\n type\n ...paragraphExtendsImageGrid_paragraph\n __typename\n }\n ...getSeriesParagraphTopSpacings_richText\n ...getPostParagraphTopSpacings_richText\n __typename\n}\n\nfragment paragraphExtendsImageGrid_paragraph on Paragraph {\n layout\n type\n __typename\n id\n}\n\nfragment getSeriesParagraphTopSpacings_richText on RichText {\n paragraphs {\n id\n __typename\n }\n sections {\n ...getSectionEndIndex_section\n __typename\n }\n __typename\n}\n\nfragment getPostParagraphTopSpacings_richText on RichText {\n paragraphs {\n type\n layout\n text\n codeBlockMetadata {\n lang\n mode\n __typename\n }\n __typename\n }\n sections {\n ...getSectionEndIndex_section\n __typename\n }\n __typename\n}\n\nfragment CardByline_post on Post {\n ...DraftStatus_post\n ...Star_post\n ...shouldShowPublishedInStatus_post\n __typename\n id\n}\n\nfragment DraftStatus_post on Post {\n id\n pendingCollection {\n id\n creator {\n id\n __typename\n }\n ...BoldCollectionName_collection\n __typename\n }\n statusForCollection\n creator {\n id\n __typename\n }\n isPublished\n __typename\n}\n\nfragment BoldCollectionName_collection on Collection {\n id\n name\n __typename\n}\n\nfragment Star_post on Post {\n id\n creator {\n id\n __typename\n }\n __typename\n}\n\nfragment shouldShowPublishedInStatus_post on Post {\n statusForCollection\n isPublished\n __typename\n id\n}\n\nfragment PostFooterActionsBar_post on Post {\n id\n visibility\n allowResponses\n postResponses {\n count\n __typename\n }\n isLimitedState\n creator {\n id\n __typename\n }\n collection {\n id\n __typename\n }\n ...MultiVote_post\n ...PostSharePopover_post\n ...OverflowMenuButtonWithNegativeSignal_post\n ...PostPageBookmarkButton_post\n __typename\n}\n\nfragment MultiVote_post on Post {\n id\n creator {\n id\n ...SusiClickable_user\n __typename\n }\n isPublished\n ...SusiClickable_post\n collection {\n id\n slug\n __typename\n }\n isLimitedState\n ...MultiVoteCount_post\n __typename\n}\n\nfragment SusiClickable_post on Post {\n id\n mediumUrl\n ...SusiContainer_post\n __typename\n}\n\nfragment SusiContainer_post on Post {\n id\n __typename\n}\n\nfragment MultiVoteCount_post on Post {\n id\n __typename\n}\n\nfragment PostSharePopover_post on Post {\n id\n mediumUrl\n title\n isPublished\n isLocked\n ...usePostUrl_post\n ...FriendLink_post\n __typename\n}\n\nfragment usePostUrl_post on Post {\n id\n creator {\n ...userUrl_user\n __typename\n id\n }\n collection {\n id\n domain\n slug\n __typename\n }\n isSeries\n mediumUrl\n sequence {\n slug\n __typename\n }\n uniqueSlug\n __typename\n}\n\nfragment FriendLink_post on Post {\n id\n ...SusiClickable_post\n ...useCopyFriendLink_post\n __typename\n}\n\nfragment useCopyFriendLink_post on Post {\n ...usePostUrl_post\n __typename\n id\n}\n\nfragment OverflowMenuButtonWithNegativeSignal_post on Post {\n id\n visibility\n ...OverflowMenuWithNegativeSignal_post\n __typename\n}\n\nfragment OverflowMenuWithNegativeSignal_post on Post {\n id\n creator {\n id\n __typename\n }\n collection {\n id\n __typename\n }\n ...OverflowMenuItemUndoClaps_post\n ...AddToCatalogBase_post\n __typename\n}\n\nfragment OverflowMenuItemUndoClaps_post on Post {\n id\n clapCount\n ...ClapMutation_post\n __typename\n}\n\nfragment ClapMutation_post on Post {\n __typename\n id\n clapCount\n ...MultiVoteCount_post\n}\n\nfragment AddToCatalogBase_post on Post {\n id\n isPublished\n __typename\n}\n\nfragment PostPageBookmarkButton_post on Post {\n ...AddToCatalogBookmarkButton_post\n __typename\n id\n}\n\nfragment AddToCatalogBookmarkButton_post on Post {\n ...AddToCatalogBase_post\n __typename\n id\n}\n\nfragment InResponseToEntityPreview_post on Post {\n id\n inResponseToEntityType\n __typename\n}\n\nfragment PostScrollTracker_post on Post {\n id\n collection {\n id\n __typename\n }\n sequence {\n sequenceId\n __typename\n }\n __typename\n}\n\nfragment HighDensityPreview_post on Post {\n id\n title\n previewImage {\n id\n focusPercentX\n focusPercentY\n __typename\n }\n extendedPreviewContent(\n truncationConfig: {previewParagraphsWordCountThreshold: 400, minimumWordLengthForTruncation: 150, truncateAtEndOfSentence: true, showFullImageCaptions: true, shortformPreviewParagraphsWordCountThreshold: 30, shortformMinimumWordLengthForTruncation: 30}\n ) {\n subtitle\n __typename\n }\n ...HighDensityFooter_post\n __typename\n}\n\nfragment HighDensityFooter_post on Post {\n id\n readingTime\n tags {\n ...TopicPill_tag\n __typename\n }\n ...BookmarkButton_post\n ...ExpandablePostCardOverflowButton_post\n ...OverflowMenuButtonWithNegativeSignal_post\n __typename\n}\n\nfragment TopicPill_tag on Tag {\n __typename\n id\n displayTitle\n normalizedTagSlug\n}\n\nfragment BookmarkButton_post on Post {\n visibility\n ...SusiClickable_post\n ...AddToCatalogBookmarkButton_post\n __typename\n id\n}\n\nfragment ExpandablePostCardOverflowButton_post on Post {\n creator {\n id\n __typename\n }\n ...ExpandablePostCardReaderButton_post\n __typename\n id\n}\n\nfragment ExpandablePostCardReaderButton_post on Post {\n id\n collection {\n id\n __typename\n }\n creator {\n id\n __typename\n }\n clapCount\n ...ClapMutation_post\n __typename\n}\n"
  29. }
  30. ];
  31. if(!tag) {
  32. bodyData = [
  33. {
  34. "operationName": "WebInlineRecommendedFeedQuery",
  35. "variables": {
  36. "forceRank": false,
  37. "paging": nextPaging,
  38. },
  39. "query": "query WebInlineRecommendedFeedQuery($paging: PagingOptions, $forceRank: Boolean) {\n webRecommendedFeed(paging: $paging, forceRank: $forceRank) {\n items {\n feedId\n ...HomeFeedItem_metadata\n post {\n ...PostPreview_post\n __typename\n }\n __typename\n }\n pagingInfo {\n next {\n limit\n to\n source\n __typename\n }\n __typename\n }\n __typename\n }\n}\n\nfragment HomeFeedItem_metadata on HomeFeedItem {\n reason\n moduleSourceEncoding\n reasonString\n postProviderExplanation {\n reason\n topic {\n name\n __typename\n }\n __typename\n }\n __typename\n}\n\nfragment PostPreview_post on Post {\n id\n creator {\n ...PostPreview_user\n __typename\n id\n }\n collection {\n ...CardByline_collection\n ...ExpandablePostByline_collection\n __typename\n id\n }\n ...InteractivePostBody_postPreview\n firstPublishedAt\n isLocked\n isSeries\n latestPublishedAt\n inResponseToCatalogResult {\n __typename\n }\n pinnedAt\n pinnedByCreatorAt\n previewImage {\n id\n focusPercentX\n focusPercentY\n __typename\n }\n readingTime\n sequence {\n slug\n __typename\n }\n title\n uniqueSlug\n ...CardByline_post\n ...PostFooterActionsBar_post\n ...InResponseToEntityPreview_post\n ...PostScrollTracker_post\n ...HighDensityPreview_post\n __typename\n}\n\nfragment PostPreview_user on User {\n __typename\n name\n username\n ...CardByline_user\n ...ExpandablePostByline_user\n id\n}\n\nfragment CardByline_user on User {\n __typename\n id\n name\n username\n mediumMemberAt\n socialStats {\n followerCount\n __typename\n }\n ...useIsVerifiedBookAuthor_user\n ...userUrl_user\n ...UserMentionTooltip_user\n}\n\nfragment useIsVerifiedBookAuthor_user on User {\n verifications {\n isBookAuthor\n __typename\n }\n __typename\n id\n}\n\nfragment userUrl_user on User {\n __typename\n id\n customDomainState {\n live {\n domain\n __typename\n }\n __typename\n }\n hasSubdomain\n username\n}\n\nfragment UserMentionTooltip_user on User {\n id\n name\n username\n bio\n imageId\n mediumMemberAt\n membership {\n tier\n __typename\n id\n }\n ...UserAvatar_user\n ...UserFollowButton_user\n ...useIsVerifiedBookAuthor_user\n __typename\n}\n\nfragment UserAvatar_user on User {\n __typename\n id\n imageId\n mediumMemberAt\n membership {\n tier\n __typename\n id\n }\n name\n username\n ...userUrl_user\n}\n\nfragment UserFollowButton_user on User {\n ...UserFollowButtonSignedIn_user\n ...UserFollowButtonSignedOut_user\n __typename\n id\n}\n\nfragment UserFollowButtonSignedIn_user on User {\n id\n name\n __typename\n}\n\nfragment UserFollowButtonSignedOut_user on User {\n id\n ...SusiClickable_user\n __typename\n}\n\nfragment SusiClickable_user on User {\n ...SusiContainer_user\n __typename\n id\n}\n\nfragment SusiContainer_user on User {\n ...SignInOptions_user\n ...SignUpOptions_user\n __typename\n id\n}\n\nfragment SignInOptions_user on User {\n id\n name\n __typename\n}\n\nfragment SignUpOptions_user on User {\n id\n name\n __typename\n}\n\nfragment ExpandablePostByline_user on User {\n __typename\n id\n name\n imageId\n ...userUrl_user\n ...useIsVerifiedBookAuthor_user\n}\n\nfragment CardByline_collection on Collection {\n name\n ...collectionUrl_collection\n __typename\n id\n}\n\nfragment collectionUrl_collection on Collection {\n id\n domain\n slug\n __typename\n}\n\nfragment ExpandablePostByline_collection on Collection {\n __typename\n id\n name\n domain\n slug\n}\n\nfragment InteractivePostBody_postPreview on Post {\n extendedPreviewContent(\n truncationConfig: {previewParagraphsWordCountThreshold: 400, minimumWordLengthForTruncation: 150, truncateAtEndOfSentence: true, showFullImageCaptions: true, shortformPreviewParagraphsWordCountThreshold: 30, shortformMinimumWordLengthForTruncation: 30}\n ) {\n bodyModel {\n ...PostBody_bodyModel\n __typename\n }\n isFullContent\n __typename\n }\n __typename\n id\n}\n\nfragment PostBody_bodyModel on RichText {\n sections {\n name\n startIndex\n textLayout\n imageLayout\n backgroundImage {\n id\n originalHeight\n originalWidth\n __typename\n }\n videoLayout\n backgroundVideo {\n videoId\n originalHeight\n originalWidth\n previewImageId\n __typename\n }\n __typename\n }\n paragraphs {\n id\n ...PostBodySection_paragraph\n __typename\n }\n ...normalizedBodyModel_richText\n __typename\n}\n\nfragment PostBodySection_paragraph on Paragraph {\n name\n ...PostBodyParagraph_paragraph\n __typename\n id\n}\n\nfragment PostBodyParagraph_paragraph on Paragraph {\n name\n type\n ...ImageParagraph_paragraph\n ...TextParagraph_paragraph\n ...IframeParagraph_paragraph\n ...MixtapeParagraph_paragraph\n ...CodeBlockParagraph_paragraph\n __typename\n id\n}\n\nfragment ImageParagraph_paragraph on Paragraph {\n href\n layout\n metadata {\n id\n originalHeight\n originalWidth\n focusPercentX\n focusPercentY\n alt\n __typename\n }\n ...Markups_paragraph\n ...ParagraphRefsMapContext_paragraph\n ...PostAnnotationsMarker_paragraph\n __typename\n id\n}\n\nfragment Markups_paragraph on Paragraph {\n name\n text\n hasDropCap\n dropCapImage {\n ...MarkupNode_data_dropCapImage\n __typename\n id\n }\n markups {\n ...Markups_markup\n __typename\n }\n __typename\n id\n}\n\nfragment MarkupNode_data_dropCapImage on ImageMetadata {\n ...DropCap_image\n __typename\n id\n}\n\nfragment DropCap_image on ImageMetadata {\n id\n originalHeight\n originalWidth\n __typename\n}\n\nfragment Markups_markup on Markup {\n type\n start\n end\n href\n anchorType\n userId\n linkMetadata {\n httpStatus\n __typename\n }\n __typename\n}\n\nfragment ParagraphRefsMapContext_paragraph on Paragraph {\n id\n name\n text\n __typename\n}\n\nfragment PostAnnotationsMarker_paragraph on Paragraph {\n ...PostViewNoteCard_paragraph\n __typename\n id\n}\n\nfragment PostViewNoteCard_paragraph on Paragraph {\n name\n __typename\n id\n}\n\nfragment TextParagraph_paragraph on Paragraph {\n type\n hasDropCap\n codeBlockMetadata {\n mode\n lang\n __typename\n }\n ...Markups_paragraph\n ...ParagraphRefsMapContext_paragraph\n __typename\n id\n}\n\nfragment IframeParagraph_paragraph on Paragraph {\n type\n iframe {\n mediaResource {\n id\n iframeSrc\n iframeHeight\n iframeWidth\n title\n __typename\n }\n __typename\n }\n layout\n ...Markups_paragraph\n __typename\n id\n}\n\nfragment MixtapeParagraph_paragraph on Paragraph {\n type\n mixtapeMetadata {\n href\n mediaResource {\n mediumCatalog {\n id\n __typename\n }\n __typename\n }\n __typename\n }\n ...GenericMixtapeParagraph_paragraph\n __typename\n id\n}\n\nfragment GenericMixtapeParagraph_paragraph on Paragraph {\n text\n mixtapeMetadata {\n href\n thumbnailImageId\n __typename\n }\n markups {\n start\n end\n type\n href\n __typename\n }\n __typename\n id\n}\n\nfragment CodeBlockParagraph_paragraph on Paragraph {\n codeBlockMetadata {\n lang\n mode\n __typename\n }\n __typename\n id\n}\n\nfragment normalizedBodyModel_richText on RichText {\n paragraphs {\n ...normalizedBodyModel_richText_paragraphs\n __typename\n }\n sections {\n startIndex\n ...getSectionEndIndex_section\n __typename\n }\n ...getParagraphStyles_richText\n ...getParagraphSpaces_richText\n __typename\n}\n\nfragment normalizedBodyModel_richText_paragraphs on Paragraph {\n markups {\n ...normalizedBodyModel_richText_paragraphs_markups\n __typename\n }\n codeBlockMetadata {\n lang\n mode\n __typename\n }\n ...getParagraphHighlights_paragraph\n ...getParagraphPrivateNotes_paragraph\n __typename\n id\n}\n\nfragment normalizedBodyModel_richText_paragraphs_markups on Markup {\n type\n __typename\n}\n\nfragment getParagraphHighlights_paragraph on Paragraph {\n name\n __typename\n id\n}\n\nfragment getParagraphPrivateNotes_paragraph on Paragraph {\n name\n __typename\n id\n}\n\nfragment getSectionEndIndex_section on Section {\n startIndex\n __typename\n}\n\nfragment getParagraphStyles_richText on RichText {\n paragraphs {\n text\n type\n __typename\n }\n sections {\n ...getSectionEndIndex_section\n __typename\n }\n __typename\n}\n\nfragment getParagraphSpaces_richText on RichText {\n paragraphs {\n layout\n metadata {\n originalHeight\n originalWidth\n id\n __typename\n }\n type\n ...paragraphExtendsImageGrid_paragraph\n __typename\n }\n ...getSeriesParagraphTopSpacings_richText\n ...getPostParagraphTopSpacings_richText\n __typename\n}\n\nfragment paragraphExtendsImageGrid_paragraph on Paragraph {\n layout\n type\n __typename\n id\n}\n\nfragment getSeriesParagraphTopSpacings_richText on RichText {\n paragraphs {\n id\n __typename\n }\n sections {\n ...getSectionEndIndex_section\n __typename\n }\n __typename\n}\n\nfragment getPostParagraphTopSpacings_richText on RichText {\n paragraphs {\n type\n layout\n text\n codeBlockMetadata {\n lang\n mode\n __typename\n }\n __typename\n }\n sections {\n ...getSectionEndIndex_section\n __typename\n }\n __typename\n}\n\nfragment CardByline_post on Post {\n ...DraftStatus_post\n ...Star_post\n ...shouldShowPublishedInStatus_post\n __typename\n id\n}\n\nfragment DraftStatus_post on Post {\n id\n pendingCollection {\n id\n creator {\n id\n __typename\n }\n ...BoldCollectionName_collection\n __typename\n }\n statusForCollection\n creator {\n id\n __typename\n }\n isPublished\n __typename\n}\n\nfragment BoldCollectionName_collection on Collection {\n id\n name\n __typename\n}\n\nfragment Star_post on Post {\n id\n creator {\n id\n __typename\n }\n __typename\n}\n\nfragment shouldShowPublishedInStatus_post on Post {\n statusForCollection\n isPublished\n __typename\n id\n}\n\nfragment PostFooterActionsBar_post on Post {\n id\n visibility\n allowResponses\n postResponses {\n count\n __typename\n }\n isLimitedState\n creator {\n id\n __typename\n }\n collection {\n id\n __typename\n }\n ...MultiVote_post\n ...PostSharePopover_post\n ...OverflowMenuButtonWithNegativeSignal_post\n ...PostPageBookmarkButton_post\n __typename\n}\n\nfragment MultiVote_post on Post {\n id\n creator {\n id\n ...SusiClickable_user\n __typename\n }\n isPublished\n ...SusiClickable_post\n collection {\n id\n slug\n __typename\n }\n isLimitedState\n ...MultiVoteCount_post\n __typename\n}\n\nfragment SusiClickable_post on Post {\n id\n mediumUrl\n ...SusiContainer_post\n __typename\n}\n\nfragment SusiContainer_post on Post {\n id\n __typename\n}\n\nfragment MultiVoteCount_post on Post {\n id\n __typename\n}\n\nfragment PostSharePopover_post on Post {\n id\n mediumUrl\n title\n isPublished\n isLocked\n ...usePostUrl_post\n ...FriendLink_post\n __typename\n}\n\nfragment usePostUrl_post on Post {\n id\n creator {\n ...userUrl_user\n __typename\n id\n }\n collection {\n id\n domain\n slug\n __typename\n }\n isSeries\n mediumUrl\n sequence {\n slug\n __typename\n }\n uniqueSlug\n __typename\n}\n\nfragment FriendLink_post on Post {\n id\n ...SusiClickable_post\n ...useCopyFriendLink_post\n ...UpsellClickable_post\n __typename\n}\n\nfragment useCopyFriendLink_post on Post {\n ...usePostUrl_post\n __typename\n id\n}\n\nfragment UpsellClickable_post on Post {\n id\n collection {\n id\n __typename\n }\n sequence {\n sequenceId\n __typename\n }\n creator {\n id\n __typename\n }\n __typename\n}\n\nfragment OverflowMenuButtonWithNegativeSignal_post on Post {\n id\n visibility\n ...OverflowMenuWithNegativeSignal_post\n __typename\n}\n\nfragment OverflowMenuWithNegativeSignal_post on Post {\n id\n creator {\n id\n __typename\n }\n collection {\n id\n __typename\n }\n ...OverflowMenuItemUndoClaps_post\n ...AddToCatalogBase_post\n __typename\n}\n\nfragment OverflowMenuItemUndoClaps_post on Post {\n id\n clapCount\n ...ClapMutation_post\n __typename\n}\n\nfragment ClapMutation_post on Post {\n __typename\n id\n clapCount\n ...MultiVoteCount_post\n}\n\nfragment AddToCatalogBase_post on Post {\n id\n isPublished\n __typename\n}\n\nfragment PostPageBookmarkButton_post on Post {\n ...AddToCatalogBookmarkButton_post\n __typename\n id\n}\n\nfragment AddToCatalogBookmarkButton_post on Post {\n ...AddToCatalogBase_post\n __typename\n id\n}\n\nfragment InResponseToEntityPreview_post on Post {\n id\n inResponseToEntityType\n __typename\n}\n\nfragment PostScrollTracker_post on Post {\n id\n collection {\n id\n __typename\n }\n sequence {\n sequenceId\n __typename\n }\n __typename\n}\n\nfragment HighDensityPreview_post on Post {\n id\n title\n previewImage {\n id\n focusPercentX\n focusPercentY\n __typename\n }\n extendedPreviewContent(\n truncationConfig: {previewParagraphsWordCountThreshold: 400, minimumWordLengthForTruncation: 150, truncateAtEndOfSentence: true, showFullImageCaptions: true, shortformPreviewParagraphsWordCountThreshold: 30, shortformMinimumWordLengthForTruncation: 30}\n ) {\n subtitle\n __typename\n }\n ...HighDensityFooter_post\n __typename\n}\n\nfragment HighDensityFooter_post on Post {\n id\n readingTime\n tags {\n ...TopicPill_tag\n __typename\n }\n ...BookmarkButton_post\n ...ExpandablePostCardOverflowButton_post\n ...OverflowMenuButtonWithNegativeSignal_post\n __typename\n}\n\nfragment TopicPill_tag on Tag {\n __typename\n id\n displayTitle\n normalizedTagSlug\n}\n\nfragment BookmarkButton_post on Post {\n visibility\n ...SusiClickable_post\n ...AddToCatalogBookmarkButton_post\n __typename\n id\n}\n\nfragment ExpandablePostCardOverflowButton_post on Post {\n creator {\n id\n __typename\n }\n ...ExpandablePostCardReaderButton_post\n __typename\n id\n}\n\nfragment ExpandablePostCardReaderButton_post on Post {\n id\n collection {\n id\n __typename\n }\n creator {\n id\n __typename\n }\n clapCount\n ...ClapMutation_post\n __typename\n}\n"
  40. }
  41. ];
  42. }
  43. let response = await fetch(`https://medium.com/_/graphql?userscript`, {
  44. method: "POST",
  45. body: JSON.stringify(bodyData),
  46. headers: {
  47. Accept: "application/json",
  48. "Content-Type": "application/json",
  49. },
  50. });
  51.  
  52. if(response.ok) {
  53. let result = await response.json();
  54.  
  55. let items;
  56. let notLockedOnes;
  57. let nextPaging;
  58. if(tag) {
  59. items = result[0].data.personalisedTagFeed.items;
  60. notLockedOnes = items.filter(item => !item.post.isLocked);
  61.  
  62. nextPaging = {...result[0].data.personalisedTagFeed.pagingInfo.next, __typename: undefined};
  63. } else {
  64. items = result[0].data.webRecommendedFeed.items;
  65. notLockedOnes = items.filter(item => !item.post.isLocked);
  66.  
  67. nextPaging = {...result[0].data.webRecommendedFeed.pagingInfo.next, __typename: undefined};
  68. }
  69.  
  70. return { items: notLockedOnes, nextPaging, originalResultCount: items.length};
  71. }
  72. }
  73.  
  74. function insertAfter(newNode, existingNode) {
  75. existingNode.parentNode.insertBefore(newNode, existingNode.nextSibling);
  76. }
  77.  
  78. (async function() {
  79. 'use strict';
  80.  
  81. let allItems = [];
  82.  
  83. let tagPart = window.location.href.match(/tag\=(.*)$/);
  84. let tag = "";
  85. if(tagPart) {
  86. tag = tagPart[1];
  87. }
  88.  
  89. let progressElement;
  90.  
  91. window.addEventListener("load", e => {
  92. setTimeout(() => {
  93. progressElement = document.createElement("div");
  94. progressElement.className = "n o ay";
  95.  
  96. let header = document.querySelector("#root").querySelector("div").querySelectorAll("div")[2].querySelector("div").querySelectorAll("div")[1];
  97. let afterThis = header.children[0];
  98.  
  99. insertAfter(progressElement, afterThis);
  100. }, 1000);
  101.  
  102. }, false);
  103.  
  104. let fullSection;
  105. setTimeout(() => {
  106. let allArticles = document.querySelectorAll("article");
  107.  
  108. console.log("Originally found", allArticles.length, "articles on the page, now removing all but first one with an image!");
  109.  
  110. // detect first article that has an image
  111. let articleToKeepIdx = -1;
  112. for(let i=0; i<allArticles.length; i++) {
  113. if(allArticles[i].querySelectorAll("img").length > 0) {
  114. articleToKeepIdx = i;
  115. break;
  116. }
  117. }
  118. console.log("First article with image is article #", articleToKeepIdx);
  119.  
  120. for(let i=allArticles.length-1; i>=0; i--) {
  121. if(i !== articleToKeepIdx) {
  122. allArticles[i].parentNode.removeChild(allArticles[i]);
  123. }
  124. }
  125.  
  126. fullSection = document.querySelector("article").parentNode.parentNode.parentNode.parentNode.parentNode;
  127. if(fullSection) {
  128. fullSection.style.visibility = "hidden";
  129. }
  130.  
  131. // Navmenu
  132. let scrollerItems = document.querySelector("#scroller-items");
  133. let links = scrollerItems.querySelectorAll("a");
  134. for(let link of links) {
  135.  
  136. if(link.href.includes("suggestions")) {
  137.  
  138. let svgpath = link.querySelector("path");
  139. svgpath.setAttribute("d", "M2,9 L17,9 L17,11 L2,11z");
  140.  
  141. let newHref = "https://medium.com/me/following";
  142. let cloneP = link.querySelector("span").cloneNode(true);
  143.  
  144. let newLink = document.createElement("a");
  145. newLink.href = newHref;
  146. newLink.target = "_blank";
  147.  
  148. newLink.appendChild(cloneP);
  149. link.parentNode.appendChild(newLink);
  150.  
  151. link.parentNode.removeChild(link);
  152. continue;
  153. }
  154. let tag = link.href.match(/tag\=(.*)\&/);
  155. if(tag) {
  156. let newHref = link.href.replace(/\&.*/, "");
  157. let cloneP = link.querySelector("button").cloneNode(true);
  158.  
  159. let newLink = document.createElement("a");
  160. newLink.href = newHref;
  161.  
  162. newLink.appendChild(cloneP);
  163. link.parentNode.appendChild(newLink);
  164.  
  165. link.parentNode.removeChild(link);
  166.  
  167. } else {
  168. let queryString = link.href.match(/\?(.*)/);
  169. if(queryString) {
  170. let queryPart = queryString[1];
  171. if(!queryPart.includes("feed=")) {
  172. let newHref = link.href.replace(/\?.*/, "");
  173. let cloneP = link.querySelector("button").cloneNode(true);
  174.  
  175. let newLink = document.createElement("a");
  176. newLink.href = newHref;
  177.  
  178. newLink.appendChild(cloneP);
  179. link.parentNode.appendChild(newLink);
  180.  
  181. link.parentNode.removeChild(link);
  182. }
  183. }
  184. }
  185. }
  186. let destNode = scrollerItems.parentNode.parentNode.parentNode.children[0];
  187. destNode.style.height = "160px";
  188. let children = scrollerItems.children;
  189. for(let child of children) {
  190. let newChild = child.cloneNode(true);
  191. newChild.style.display = "inline-block";
  192. destNode.appendChild(newChild);
  193. }
  194. for(let i=children.length-1; i>=0; i--) {
  195. let child = children[i];
  196. child.parentNode.removeChild(child);
  197. }
  198. for(let i=scrollerItems.parentNode.parentNode.children.length-1; i>=0; i--) {
  199. let child = scrollerItems.parentNode.parentNode.children[i];
  200. child.parentNode.removeChild(child);
  201. }
  202.  
  203. // top HOME link
  204. let topHomeLink = document.querySelector("[class='n o ay']").querySelector("a");
  205. let cloneContent = topHomeLink.querySelector("svg").cloneNode(true);
  206.  
  207. let newLink = document.createElement("a");
  208. newLink.href = "https://medium.com";
  209.  
  210. newLink.appendChild(cloneContent);
  211. topHomeLink.parentNode.prepend(newLink);
  212. topHomeLink.parentNode.removeChild(topHomeLink);
  213.  
  214.  
  215. }, 2000);
  216.  
  217.  
  218. // FETCH articles
  219.  
  220. let nextPaging = { limit: 25};
  221. let lastPageFound = false;
  222. let i=0;
  223.  
  224. while(!lastPageFound) {
  225. if(progressElement) {
  226. progressElement.innerText = `Fetching ${i+1} ...`;
  227. }
  228. console.log("Fetching round", i+1);
  229. let result = await fetchFeed(nextPaging, tag);
  230.  
  231. allItems = [...allItems, ...result.items];
  232.  
  233. console.log("Fetched", result.originalResultCount, "results");
  234. if(result.originalResultCount < 25) {
  235. lastPageFound = true;
  236. }
  237.  
  238. nextPaging = result.nextPaging;
  239. i++;
  240. }
  241.  
  242. if(progressElement) {
  243. await makeOutputPossible(10);
  244. progressElement.innerText = `Sorting...`;
  245. await makeOutputPossible(10);
  246. }
  247. allItems.sort((a, b) => {
  248. if(a.post.latestPublishedAt > b.post.latestPublishedAt) {
  249. return -1;
  250. }
  251. if(a.post.latestPublishedAt < b.post.latestPublishedAt) {
  252. return 1;
  253. }
  254. return 0;
  255.  
  256. });
  257. console.log("All items after 15 rounds:", allItems);
  258.  
  259. let articles = document.querySelectorAll("article");
  260. let maxheightparent = articles[0].parentNode.parentNode.parentNode.parentNode;
  261. let outerparent = maxheightparent.parentNode;
  262.  
  263. maxheightparent.style.maxHeight = "unset";
  264.  
  265. if(progressElement) {
  266. await makeOutputPossible(10);
  267. progressElement.innerText = `Creating articles...`;
  268. await makeOutputPossible(10);
  269. }
  270.  
  271. for(let i=outerparent.children.length-2; i>0; i--) {
  272. outerparent.removeChild(outerparent.children[i]);
  273. }
  274.  
  275. for(let i=1; i<allItems.length; i++) {
  276. let articleClone = articles[0].cloneNode(true);
  277.  
  278. articles[0].parentNode.appendChild(articleClone);
  279. }
  280. articles = document.querySelectorAll("article");
  281. console.log("After cloning there are now", articles.length, "articles on the page");
  282.  
  283. if(progressElement) {
  284. await makeOutputPossible(10);
  285. progressElement.innerText = `Filling up articles...`;
  286. await makeOutputPossible(10);
  287. }
  288. // replacing existing articles
  289. for(let i=0; i<articles.length; i++) {
  290. console.log("Changing article", i+1);
  291. let block = articles[i].querySelector("div").querySelector("div").querySelector("div").querySelector("div").querySelector("div").querySelector("div");
  292.  
  293. let authorSection = block.children[0].children[0].children[1].children[0].children[0];
  294. let authorElement;
  295. if(authorSection) {
  296. authorElement = authorSection.querySelector("p");
  297. } else {
  298. // AUTHOR is in a block with "IMG In SECTION by AUTHOR"
  299. authorElement = block.children[0].children[0].children[4].children[0].children[0].querySelector("p");
  300. }
  301.  
  302. let authorExtra = authorElement.parentNode.parentNode.parentNode.parentNode;
  303. if(authorExtra.children.length > 2) {
  304. authorExtra.children[0].parentNode.removeChild(authorExtra.children[0]);
  305. authorExtra.children[0].parentNode.removeChild(authorExtra.children[0]);
  306. authorExtra.children[0].parentNode.removeChild(authorExtra.children[0]);
  307. }
  308.  
  309.  
  310. let thedateSpans = block.children[1].children[0].children[1].children[0].children[0].children[0].children[0].querySelectorAll("span");
  311. let thedateSpan;
  312.  
  313. let ago = false;
  314. console.log("Found spans:", thedateSpans.length);
  315. if(thedateSpans.length) {
  316. thedateSpan = thedateSpans[0];
  317. } else {
  318. thedateSpan = block.children[1].children[0].children[1].children[0].children[0].children[0].children[0];
  319. console.log("Identified AGO date element:", thedateSpan.childNodes);
  320. ago = true;
  321. }
  322. let childrenCount = thedateSpan.parentNode.children.length;
  323. if(childrenCount === 3) { // Paywall symbol
  324. thedateSpan.parentNode.removeChild(thedateSpan.parentNode.children[0]);
  325. }
  326. let thedateElement = thedateSpan.childNodes[0];
  327. if(ago) {
  328. thedateElement = thedateSpan.childNodes[1];
  329. }
  330. console.log("Found date element:", thedateElement);
  331. if(!ago) {
  332. let dateExtra = thedateSpan.parentNode.parentNode.children[1];
  333. if(dateExtra) {
  334. dateExtra.parentNode.removeChild(dateExtra);
  335. }
  336. } else {
  337. let dateExtra = thedateSpan.querySelector("div");
  338. if(dateExtra) {
  339. dateExtra.parentNode.removeChild(dateExtra);
  340. }
  341. }
  342. let dateExtra = thedateSpan.parentNode.children[1];
  343. if(dateExtra) {
  344. dateExtra.parentNode.removeChild(dateExtra);
  345. }
  346.  
  347. let article = block.children[1].querySelector("div").querySelector("div");
  348. let imageElement = article.parentNode.parentNode.children[1]?.querySelectorAll("img")[1];
  349.  
  350. //console.log(imageElement);
  351.  
  352. let miniImage = authorElement.parentNode.parentNode.parentNode.parentNode.parentNode.children[0].children[0];
  353. miniImage.parentNode.removeChild(miniImage);
  354.  
  355. // let bottomLine = article.parentNode.children[1];
  356. // bottomLine.removeChild(bottomLine.children[0]);
  357.  
  358.  
  359. let titleElement = article.querySelector("h2");
  360. let paragraphElement = article.querySelector("h3");
  361. let link = document.createElement("a");
  362. link.target = "_blank";
  363.  
  364. let newParagraph = paragraphElement.cloneNode(true);
  365. let newTitle = titleElement.cloneNode(true);
  366.  
  367. paragraphElement.parentNode.removeChild(paragraphElement);
  368. titleElement.parentNode.removeChild(titleElement);
  369.  
  370. if(newTitle) {
  371. newTitle.innerText = allItems[i].post.title;
  372. }
  373. if(newParagraph) {
  374. newParagraph.innerText = allItems[i].post.extendedPreviewContent.subtitle;
  375. }
  376. link.href = allItems[i].post.mediumUrl;
  377.  
  378. if(authorElement) {
  379. authorElement.innerText = allItems[i].post.creator.name;
  380.  
  381. let authorlink = authorElement.parentNode;
  382. authorlink.href = "/@" + allItems[i].post.creator.username;
  383. authorlink.target = "_blank";
  384. authorlink.rel = "";
  385. }
  386. if(thedateElement) {
  387. thedateElement.textContent = (new Date(allItems[i].post.latestPublishedAt)).toLocaleString();
  388. }
  389.  
  390. if(imageElement && allItems[i].post.previewImage?.id) {
  391. let imageElementClone = imageElement.cloneNode(true);
  392. imageElementClone.src = `https://miro.medium.com/v2/resize:fill:112:112/${allItems[i].post.previewImage.id}`;
  393. // let imageParent = imageElement.parentNode.parentNode.parentNode;
  394. // let newImageLink = document.createElement("a");
  395. // newImageLink.href = allItems[i].post.mediumUrl;
  396.  
  397. // newImageLink.appendChild(imageElementClone);
  398. // imageParent.appendChild(newImageLink);
  399.  
  400. imageElement.parentNode.appendChild(imageElementClone);
  401. imageElement.parentNode.removeChild(imageElement);
  402. }
  403.  
  404. link.appendChild(newTitle);
  405. link.appendChild(newParagraph);
  406. article.appendChild(link);
  407. }
  408.  
  409. if(progressElement) {
  410. progressElement.style.display = "none";
  411. }
  412.  
  413. if(fullSection) {
  414. fullSection.style.visibility = "visible";
  415. }
  416. })();
  417.  
  418.