lens-protocol / lens-sdk

The official SDK to interact with the Lens Protocol
https://docs.lens.xyz/docs/what-is-lens
MIT License
231 stars 72 forks source link

Cache in Queries #419

Closed jsonpreet closed 7 months ago

jsonpreet commented 1 year ago

Describe the bug Is there a cache in queries, when I change the route or click on another link and go back, the Component is re-rendered but the hook is not called.

Version of the packages you are using 1.2.1

cesarenaldi commented 1 year ago

Hey @jsonpreet,

under the hood Lens SDK uses Apollo Client which has its own cache where the vast majority of the Lens UI state lives (also inside few Apollo Reactive Variables but these are transparently merged into cache items one way or the other).

What you are describing is an expected behaviour: changing routes should leverage cached content for a fast UX.

Can you please clarify a little more about your scenario so we can understand the issue you are seeing and what you expect instead?

jsonpreet commented 1 year ago

I am using react-router-dom for navigation and when I switch to another route the component is re-rendered but it won't request useFeed or any other lens-client/react-web hook

cesarenaldi commented 1 year ago

If the hook arguments are not changed that is expected. Please advise if arguments are changing (e.g. a different profile ID).

This allows to accommodate the needs of apps that want to leverage the client cache for a snappy SPA experience. You will notice for example that if you come from a list of items (profiles or publications) and click through a page that shows the details of one of such items, the corresponding single item hook won't make another request but leverage the content of the cache.

One can fetch newer results on demand via the prev callback. You can see in this example used to create a "fetch newer" result feature (but can be called programmatically from a useEffect if need to).

jsonpreet commented 1 year ago

That's not what I am saying, I mean When I click on Profile it loads profile data with publications and I click on Home or Go back from Browser on the home page it shows prev data, the hook is not called/re-rendered. And again if I click on the Same profile it also shows prev (cached) data. I have to hard reload the tab or window for new data.

cesarenaldi commented 1 year ago

I see now. I tried a couple of changes and found an approach that I think should work without introducing unsolicited request in case the component gets re-rendered (no unmounted and render it again).

We should be able to get something in the next release to address this. I'll get back to you on this thread once it's ready for you to try.

jsonpreet commented 1 year ago

Ok Thanks

cesarenaldi commented 1 year ago

@jsonpreet to keep you in the loop on how we are solving this issue.

After evaluating different scenarios and usages of the Lens SDK we had to implement 2 different strategy.

One strategy is the one that applies to "read hooks" that return a single entity (e.g. usePublication, useProfile). For these hooks in case of full re-render, the data cached will be used to show immediate content to the consumer while under the hook a network request will fire to update the data. Once such request is completed the data will be updated immediately.

This provides the best of both world, immediate rendering with local data and fresh data from the server in the re-rendering scenario.

The second strategies applies to paginated read hooks (e.g. useFeed,usePublications, useExplorePublications, etc.). Here simply refetching the data is not possible as we need to take into account pagination constrainsts. In the specific the fact that in the previous render the user might have navigated several pages. Refetching page 1 alone would cause an unpleasant UX in terms jump and restoring of scroll position. Refetching the entire results set in one single request is not feasible due to the 50 items max limit per page we have from the API. Refetching all pages one after the other risks again to impact the restoring of scroll position as it's a race condition between browser heuristic logic and several API requests.

So what we are doing in this case is expose another property from such hooks. This property is called beforeCount and it can be used to implement UX similar to Twitter

image

Once the consumer is notified of newer items available (beforeCount > 0) then it's up to them to call the prev to get fresher data. This can be done involving the user in a Twitter-style UX or by pragmatically calling the prev callback in a useEffect.

moesalih commented 1 year ago

hi @cesarenaldi . I'm curious about this new beforeCount property. How does the sdk know that there are newer items to populate beforeCount when the hook is called again without doing any fetching?

cesarenaldi commented 1 year ago

hi @cesarenaldi . I'm curious about this new beforeCount property. How does the sdk know that there are newer items to populate beforeCount when the hook is called again without doing any fetching?

Under the hood it performs a "probing" request using the prev cursor but instead of merging the resulting items into the current results set it to populate a client only beforeCount field.

To achieve a Twitter-like UX one just need to:

  const { data, beforeCount, prev... } = usePublications(...);

  ...

  {beforeCount > 0 && (
    <button onClick={prev}>
      Show {beforeCount} publications
    </button>
  )}
moesalih commented 1 year ago

Under the hood it performs a "probing" request using the prev cursor but instead of merging the resulting items into the current results set it to populate a client only beforeCount field.

This probing request doesn't include full data so it can't be merged with local results? Just trying to understand how things work.

And thank you for the sample, we'll try that when the sdk is updated with the new functionality.

cesarenaldi commented 1 year ago

This probing request doesn't include full data

Some context. The pagination in the SDK is handled as infinite pagination in both directions where new pages are appended or prepended to the current list on the client depending if the request is using the prev or the next cursor.

Now the first issue is about how many items we can retrieve in a single request. The Lens API has an hard limit of 50 items per page. Let's say an app as `limit set to 10 items per page and on such app the user engages with the pagination so to download 6 pages of result (60 items in total). There is no way the probe request can ask 60 items due the API hard limit. For sure we can coordinate with the API and up the limit but we are just shifting the problem.

The other issue is the intrinsic nature of the probe request. Even if it were to be possible through ad-hoc query parameters, if the probe request were to ask for the same exact 60 items it would fail the point of making the request.

The probe request aims at knowing about new items. The probe request we are doing uses the prev cursor so it asks for results earlier than the most recent item in the current client results set. Prepending these items to the results set will cause a re-render that is not compatible with what the user might have seen when previously scrolling the same results, this is the issue with impacting the restoring of scroll position I was mentioning.

moesalih commented 1 year ago

@cesarenaldi understood. thank you.

cesarenaldi commented 1 year ago

The solution mentioned above is now available under the next tag, specifically @lens-protocol/react-web@1.3.0-next.6.

Over time we will make the beforeCount be more dynamic. We will actively watch for newer results and change its value to reflect the number of new results available even if the hook wasn't re-rendered.

cesarenaldi commented 1 year ago

@jsonpreet and @moesalih can you please give a try to the solution mentioned above?

@moesalih in the other issue that reference this you said "both works". I wasn't sure if you tested just the single usePublication hook in the context of the notifications workaround or also the solution for paginated hooks (the one using beforeCount). Can you please clarify?

moesalih commented 1 year ago

@cesarenaldi yeah i've only tested the single usePublication in the context of the mirror notifications. i'll try the beforeCount solution for feeds soon.

moesalih commented 1 year ago

So what we are doing in this case is expose another property from such hooks. This property is called beforeCount and it can be used to implement UX similar to Twitter image

Once the consumer is notified of newer items available (beforeCount > 0) then it's up to them to call the prev to get fresher data. This can be done involving the user in a Twitter-style UX or by pragmatically calling the prev callback in a useEffect.

hi @cesarenaldi, i tried the beforeCount solution like you suggested in a useEffect and it's not working correctly. this is what i'm doing:

    const { data, hasMore, loading, next, prev, beforeCount } = useFeed(request)
    useEffect(() => {
        console.log('useFeed', { beforeCount })
        if (beforeCount > 0) prev()
    }, [beforeCount])

What happens is if I come back to the home feed and the beforeCount is greater than 0, prev gets called and beforeCount goes to 0, but the data stays the same.

Am i using it correctly? or is there a bug?

cesarenaldi commented 1 year ago

hey @moesalih, would you be able to copy here the API requests and responses you have in your dev tools involving the feed query?

Seems the API pagination issue we are aware of here: https://github.com/lens-protocol/lens-sdk/pull/431

moesalih commented 1 year ago

@cesarenaldi i'll try to reproduce but it'll be difficult because there's actually another issue where the beforeCount doesn't increment above 0 even though i know for a fact that there is a new item in the feed and if i refresh i do get that new item.

moesalih commented 1 year ago

i just noticed that the request the sdk is sending to update beforeCount is exactly the same as the request when prev is called. that shouldn't be the case right? why is the sdk sending 2 similar requests. example:

{"operationName":"Feed","variables":{"mediaTransformPublicationSmall":{"width":"350px","height":"auto","keepAspectRatio":true},"mediaTransformPublicationMedium":{"width":"700px","height":"auto","keepAspectRatio":true},"mediaTransformProfileThumbnail":{"width":"256px","height":"auto","keepAspectRatio":true},"metadata":{},"restrictEventTypesTo":["POST","COMMENT","MIRROR","COLLECT_POST","COLLECT_COMMENT"],"profileId":"0x2b9a","observerId":"0x2b9a","limit":50,"sources":[],"cursor":"{\"entityIdentifier\":\"\",\"timestamp\":1692123910,\"filter\":\"{\\\"mainContentFocus\\\":[\\\"ARTICLE\\\",\\\"AUDIO\\\",\\\"VIDEO\\\",\\\"EMBED\\\",\\\"IMAGE\\\",\\\"LINK\\\",\\\"TEXT_ONLY\\\"]}\",\"cursorDirection\":\"BEFORE\"}"},"query":"query Feed($profileId: ProfileId!, $restrictEventTypesTo: [FeedEventItemType!], $observerId: ProfileId, $limit: LimitScalar!, $cursor: Cursor, $sources: [Sources!]!, $metadata: PublicationMetadataFilters, $mediaTransformPublicationSmall: MediaTransformParams = {}, $mediaTransformPublicationMedium: MediaTransformParams = {}, $mediaTransformProfileThumbnail: MediaTransformParams = {}) {\n  result: feed(\n    request: {profileId: $profileId, feedEventItemTypes: $restrictEventTypesTo, limit: $limit, cursor: $cursor, sources: $sources, metadata: $metadata}\n  ) {\n    items {\n      ...FeedItem\n      __typename\n    }\n    pageInfo {\n      ...PaginatedResultInfo\n      __typename\n    }\n    __typename\n  }\n}\n\nfragment FeedItem on FeedItem {\n  __typename\n  root {\n    ... on Post {\n      ...Post\n      __typename\n    }\n    ... on Comment {\n      ...Comment\n      __typename\n    }\n    __typename\n  }\n  comments {\n    ...Comment\n    __typename\n  }\n  electedMirror {\n    ...ElectedMirror\n    __typename\n  }\n  mirrors {\n    ...MirrorEvent\n    __typename\n  }\n  collects {\n    ...CollectedEvent\n    __typename\n  }\n  reactions {\n    ...ReactionEvent\n    __typename\n  }\n}\n\nfragment Post on Post {\n  __typename\n  id\n  appId\n  stats {\n    ...PublicationStats\n    __typename\n  }\n  metadata {\n    ...MetadataOutput\n    __typename\n  }\n  profile {\n    ...Profile\n    __typename\n  }\n  collectedBy {\n    ...Wallet\n    __typename\n  }\n  collectModule {\n    ... on AaveFeeCollectModuleSettings {\n      ...AaveFeeCollectModuleSettings\n      __typename\n    }\n    ... on ERC4626FeeCollectModuleSettings {\n      ...Erc4626FeeCollectModuleSettings\n      __typename\n    }\n    ... on MultirecipientFeeCollectModuleSettings {\n      ...MultirecipientFeeCollectModuleSettings\n      __typename\n    }\n    ... on UnknownCollectModuleSettings {\n      ...UnknownCollectModuleSettings\n      __typename\n    }\n    ... on FreeCollectModuleSettings {\n      ...FreeCollectModuleSettings\n      __typename\n    }\n    ... on FeeCollectModuleSettings {\n      ...FeeCollectModuleSettings\n      __typename\n    }\n    ... on LimitedFeeCollectModuleSettings {\n      ...LimitedFeeCollectModuleSettings\n      __typename\n    }\n    ... on LimitedTimedFeeCollectModuleSettings {\n      ...LimitedTimedFeeCollectModuleSettings\n      __typename\n    }\n    ... on RevertCollectModuleSettings {\n      ...RevertCollectModuleSettings\n      __typename\n    }\n    ... on TimedFeeCollectModuleSettings {\n      ...TimedFeeCollectModuleSettings\n      __typename\n    }\n    ... on SimpleCollectModuleSettings {\n      ...SimpleCollectModuleSettings\n      __typename\n    }\n    __typename\n  }\n  collectNftAddress\n  referenceModule {\n    ... on FollowOnlyReferenceModuleSettings {\n      ...FollowOnlyReferenceModuleSettings\n      __typename\n    }\n    ... on DegreesOfSeparationReferenceModuleSettings {\n      ...DegreesOfSeparationReferenceModuleSettings\n      __typename\n    }\n    ... on UnknownReferenceModuleSettings {\n      ...UnknownReferenceModuleSettings\n      __typename\n    }\n    __typename\n  }\n  createdAt\n  hidden\n  isGated\n  reaction(request: {profileId: $observerId})\n  hasCollectedByMe\n  canComment(profileId: $observerId) {\n    result\n    __typename\n  }\n  canMirror(profileId: $observerId) {\n    result\n    __typename\n  }\n  canObserverDecrypt: canDecrypt(profileId: $observerId) {\n    result\n    reasons\n    __typename\n  }\n  mirrors(by: $observerId)\n  notInterested(by: $observerId)\n  bookmarked(by: $observerId)\n}\n\nfragment PublicationStats on PublicationStats {\n  __typename\n  totalAmountOfMirrors\n  totalUpvotes\n  totalDownvotes\n  totalAmountOfCollects\n  totalAmountOfComments\n  totalBookmarks\n  commentsCount: commentsTotal(forSources: $sources)\n}\n\nfragment MetadataOutput on MetadataOutput {\n  __typename\n  animatedUrl\n  content\n  contentWarning\n  description\n  image\n  locale\n  mainContentFocus\n  name\n  media {\n    ...PublicationMediaSet\n    __typename\n  }\n  attributes {\n    ...MetadataAttributeOutput\n    __typename\n  }\n  encryptionParams {\n    ...EncryptionParamsOutput\n    __typename\n  }\n  tags\n}\n\nfragment PublicationMediaSet on MediaSet {\n  __typename\n  original {\n    ...Media\n    __typename\n  }\n  optimized {\n    ...Media\n    __typename\n  }\n  small: transformed(params: $mediaTransformPublicationSmall) {\n    ...Media\n    __typename\n  }\n  medium: transformed(params: $mediaTransformPublicationMedium) {\n    ...Media\n    __typename\n  }\n}\n\nfragment Media on Media {\n  __typename\n  altTag\n  cover\n  mimeType\n  url\n}\n\nfragment MetadataAttributeOutput on MetadataAttributeOutput {\n  __typename\n  traitType\n  value\n}\n\nfragment EncryptionParamsOutput on EncryptionParamsOutput {\n  __typename\n  accessCondition {\n    ...RootConditionOutput\n    __typename\n  }\n  encryptionProvider\n  encryptedFields {\n    ...EncryptedFieldsOutput\n    __typename\n  }\n  providerSpecificParams {\n    encryptionKey\n    __typename\n  }\n}\n\nfragment RootConditionOutput on AccessConditionOutput {\n  __typename\n  or {\n    criteria {\n      ...AnyConditionOutput\n      __typename\n    }\n    __typename\n  }\n}\n\nfragment AnyConditionOutput on AccessConditionOutput {\n  __typename\n  ...LeafConditionOutput\n  or {\n    ...OrConditionOutput\n    __typename\n  }\n  and {\n    ...AndConditionOutput\n    __typename\n  }\n}\n\nfragment LeafConditionOutput on AccessConditionOutput {\n  __typename\n  nft {\n    ...NftOwnershipOutput\n    __typename\n  }\n  token {\n    ...Erc20OwnershipOutput\n    __typename\n  }\n  eoa {\n    ...EoaOwnershipOutput\n    __typename\n  }\n  profile {\n    ...ProfileOwnershipOutput\n    __typename\n  }\n  follow {\n    ...FollowConditionOutput\n    __typename\n  }\n  collect {\n    ...CollectConditionOutput\n    __typename\n  }\n}\n\nfragment NftOwnershipOutput on NftOwnershipOutput {\n  __typename\n  contractAddress\n  chainID\n  contractType\n  tokenIds\n}\n\nfragment Erc20OwnershipOutput on Erc20OwnershipOutput {\n  __typename\n  amount\n  chainID\n  condition\n  contractAddress\n  decimals\n  name\n  symbol\n}\n\nfragment EoaOwnershipOutput on EoaOwnershipOutput {\n  __typename\n  address\n}\n\nfragment ProfileOwnershipOutput on ProfileOwnershipOutput {\n  __typename\n  profileId\n}\n\nfragment FollowConditionOutput on FollowConditionOutput {\n  __typename\n  profileId\n}\n\nfragment CollectConditionOutput on CollectConditionOutput {\n  __typename\n  publicationId\n  thisPublication\n}\n\nfragment OrConditionOutput on OrConditionOutput {\n  __typename\n  criteria {\n    ...LeafConditionOutput\n    __typename\n  }\n}\n\nfragment AndConditionOutput on AndConditionOutput {\n  __typename\n  criteria {\n    ...LeafConditionOutput\n    __typename\n  }\n}\n\nfragment EncryptedFieldsOutput on EncryptedFieldsOutput {\n  __typename\n  animation_url\n  content\n  external_url\n  image\n  media {\n    original {\n      ...EncryptedMedia\n      __typename\n    }\n    __typename\n  }\n}\n\nfragment EncryptedMedia on EncryptedMedia {\n  __typename\n  altTag\n  cover\n  mimeType\n  url\n}\n\nfragment Profile on Profile {\n  ...ProfileFields\n  invitedBy {\n    ...ProfileFields\n    __typename\n  }\n  __typename\n}\n\nfragment ProfileFields on Profile {\n  __typename\n  id\n  name\n  bio\n  handle\n  ownedBy\n  interests\n  followNftAddress\n  picture {\n    ... on NftImage {\n      ...NftImage\n      __typename\n    }\n    ... on MediaSet {\n      ...ProfilePictureSet\n      __typename\n    }\n    __typename\n  }\n  coverPicture {\n    ... on NftImage {\n      ...NftImage\n      __typename\n    }\n    ... on MediaSet {\n      ...ProfileCoverSet\n      __typename\n    }\n    __typename\n  }\n  stats {\n    ...ProfileStats\n    __typename\n  }\n  followModule {\n    ... on FeeFollowModuleSettings {\n      ...FeeFollowModuleSettings\n      __typename\n    }\n    ... on ProfileFollowModuleSettings {\n      ...ProfileFollowModuleSettings\n      __typename\n    }\n    ... on RevertFollowModuleSettings {\n      ...RevertFollowModuleSettings\n      __typename\n    }\n    ... on UnknownFollowModuleSettings {\n      ...UnknownFollowModuleSettings\n      __typename\n    }\n    __typename\n  }\n  __attributes: attributes {\n    ...Attribute\n    __typename\n  }\n  dispatcher {\n    address\n    canUseRelay\n    __typename\n  }\n  onChainIdentity {\n    proofOfHumanity\n    ens {\n      name\n      __typename\n    }\n    sybilDotOrg {\n      verified\n      source {\n        twitter {\n          handle\n          __typename\n        }\n        __typename\n      }\n      __typename\n    }\n    worldcoin {\n      isHuman\n      __typename\n    }\n    __typename\n  }\n  isFollowedByMe\n  isFollowingObserver: isFollowing(who: $observerId)\n}\n\nfragment NftImage on NftImage {\n  __typename\n  contractAddress\n  tokenId\n  uri\n  verified\n}\n\nfragment ProfilePictureSet on MediaSet {\n  __typename\n  original {\n    ...Media\n    __typename\n  }\n  optimized {\n    ...Media\n    __typename\n  }\n  thumbnail: transformed(params: $mediaTransformProfileThumbnail) {\n    ...Media\n    __typename\n  }\n}\n\nfragment ProfileCoverSet on MediaSet {\n  __typename\n  original {\n    ...Media\n    __typename\n  }\n  optimized {\n    ...Media\n    __typename\n  }\n}\n\nfragment ProfileStats on ProfileStats {\n  __typename\n  totalCollects\n  totalComments\n  totalFollowers\n  totalFollowing\n  totalMirrors\n  totalPosts\n  totalPublications\n  commentsCount: commentsTotal(forSources: $sources)\n  postsCount: postsTotal(forSources: $sources)\n  mirrorsCount: mirrorsTotal(forSources: $sources)\n}\n\nfragment FeeFollowModuleSettings on FeeFollowModuleSettings {\n  __typename\n  amount {\n    ...ModuleFeeAmount\n    __typename\n  }\n  contractAddress\n  recipient\n}\n\nfragment ModuleFeeAmount on ModuleFeeAmount {\n  __typename\n  asset {\n    ...Erc20Fields\n    __typename\n  }\n  value\n}\n\nfragment Erc20Fields on Erc20 {\n  __typename\n  name\n  symbol\n  decimals\n  address\n}\n\nfragment ProfileFollowModuleSettings on ProfileFollowModuleSettings {\n  __typename\n  contractAddress\n}\n\nfragment RevertFollowModuleSettings on RevertFollowModuleSettings {\n  __typename\n  contractAddress\n}\n\nfragment UnknownFollowModuleSettings on UnknownFollowModuleSettings {\n  __typename\n  contractAddress\n}\n\nfragment Attribute on Attribute {\n  __typename\n  displayType\n  key\n  value\n}\n\nfragment Wallet on Wallet {\n  __typename\n  address\n  defaultProfile {\n    ...Profile\n    __typename\n  }\n}\n\nfragment AaveFeeCollectModuleSettings on AaveFeeCollectModuleSettings {\n  __typename\n  amount {\n    ...ModuleFeeAmount\n    __typename\n  }\n  collectLimitOptional: collectLimit\n  contractAddress\n  followerOnly\n  endTimestampOptional: endTimestamp\n  recipient\n  referralFee\n}\n\nfragment Erc4626FeeCollectModuleSettings on ERC4626FeeCollectModuleSettings {\n  __typename\n  amount {\n    ...ModuleFeeAmount\n    __typename\n  }\n  collectLimitOptional: collectLimit\n  contractAddress\n  followerOnly\n  endTimestampOptional: endTimestamp\n  recipient\n  referralFee\n  vault\n}\n\nfragment MultirecipientFeeCollectModuleSettings on MultirecipientFeeCollectModuleSettings {\n  __typename\n  amount {\n    ...ModuleFeeAmount\n    __typename\n  }\n  collectLimitOptional: collectLimit\n  contractAddress\n  followerOnly\n  endTimestampOptional: endTimestamp\n  recipients {\n    recipient\n    split\n    __typename\n  }\n  referralFee\n}\n\nfragment UnknownCollectModuleSettings on UnknownCollectModuleSettings {\n  __typename\n  contractAddress\n  collectModuleReturnData\n}\n\nfragment FreeCollectModuleSettings on FreeCollectModuleSettings {\n  __typename\n  contractAddress\n  followerOnly\n}\n\nfragment FeeCollectModuleSettings on FeeCollectModuleSettings {\n  __typename\n  amount {\n    ...ModuleFeeAmount\n    __typename\n  }\n  contractAddress\n  followerOnly\n  recipient\n  referralFee\n}\n\nfragment LimitedFeeCollectModuleSettings on LimitedFeeCollectModuleSettings {\n  __typename\n  amount {\n    ...ModuleFeeAmount\n    __typename\n  }\n  collectLimit\n  contractAddress\n  followerOnly\n  recipient\n  referralFee\n}\n\nfragment LimitedTimedFeeCollectModuleSettings on LimitedTimedFeeCollectModuleSettings {\n  __typename\n  amount {\n    ...ModuleFeeAmount\n    __typename\n  }\n  collectLimit\n  contractAddress\n  followerOnly\n  endTimestamp\n  recipient\n  referralFee\n}\n\nfragment RevertCollectModuleSettings on RevertCollectModuleSettings {\n  __typename\n  contractAddress\n}\n\nfragment TimedFeeCollectModuleSettings on TimedFeeCollectModuleSettings {\n  __typename\n  amount {\n    ...ModuleFeeAmount\n    __typename\n  }\n  contractAddress\n  followerOnly\n  endTimestamp\n  recipient\n  referralFee\n}\n\nfragment SimpleCollectModuleSettings on SimpleCollectModuleSettings {\n  __typename\n  contractAddress\n  followerOnly\n  feeOptional: fee {\n    amount {\n      ...ModuleFeeAmount\n      __typename\n    }\n    referralFee\n    recipient\n    __typename\n  }\n  collectLimitOptional: collectLimit\n  endTimestampOptional: endTimestamp\n}\n\nfragment FollowOnlyReferenceModuleSettings on FollowOnlyReferenceModuleSettings {\n  __typename\n  contractAddress\n}\n\nfragment DegreesOfSeparationReferenceModuleSettings on DegreesOfSeparationReferenceModuleSettings {\n  __typename\n  contractAddress\n  commentsRestricted\n  degreesOfSeparation\n  mirrorsRestricted\n}\n\nfragment UnknownReferenceModuleSettings on UnknownReferenceModuleSettings {\n  __typename\n  contractAddress\n  referenceModuleReturnData\n}\n\nfragment Comment on Comment {\n  __typename\n  ...CommentBase\n  commentOn {\n    ... on Post {\n      ...Post\n      __typename\n    }\n    ... on Mirror {\n      ...MirrorBase\n      __typename\n    }\n    ... on Comment {\n      ...CommentBase\n      __typename\n    }\n    __typename\n  }\n  mainPost {\n    ... on Post {\n      ...Post\n      __typename\n    }\n    ... on Mirror {\n      ...MirrorBase\n      __typename\n    }\n    __typename\n  }\n  firstComment {\n    ...CommentBase\n    __typename\n  }\n}\n\nfragment CommentBase on Comment {\n  __typename\n  id\n  appId\n  stats {\n    ...PublicationStats\n    __typename\n  }\n  metadata {\n    ...MetadataOutput\n    __typename\n  }\n  profile {\n    ...Profile\n    __typename\n  }\n  collectedBy {\n    ...Wallet\n    __typename\n  }\n  collectModule {\n    ... on AaveFeeCollectModuleSettings {\n      ...AaveFeeCollectModuleSettings\n      __typename\n    }\n    ... on ERC4626FeeCollectModuleSettings {\n      ...Erc4626FeeCollectModuleSettings\n      __typename\n    }\n    ... on MultirecipientFeeCollectModuleSettings {\n      ...MultirecipientFeeCollectModuleSettings\n      __typename\n    }\n    ... on UnknownCollectModuleSettings {\n      ...UnknownCollectModuleSettings\n      __typename\n    }\n    ... on FreeCollectModuleSettings {\n      ...FreeCollectModuleSettings\n      __typename\n    }\n    ... on FeeCollectModuleSettings {\n      ...FeeCollectModuleSettings\n      __typename\n    }\n    ... on LimitedFeeCollectModuleSettings {\n      ...LimitedFeeCollectModuleSettings\n      __typename\n    }\n    ... on LimitedTimedFeeCollectModuleSettings {\n      ...LimitedTimedFeeCollectModuleSettings\n      __typename\n    }\n    ... on RevertCollectModuleSettings {\n      ...RevertCollectModuleSettings\n      __typename\n    }\n    ... on TimedFeeCollectModuleSettings {\n      ...TimedFeeCollectModuleSettings\n      __typename\n    }\n    ... on SimpleCollectModuleSettings {\n      ...SimpleCollectModuleSettings\n      __typename\n    }\n    __typename\n  }\n  collectNftAddress\n  referenceModule {\n    ... on FollowOnlyReferenceModuleSettings {\n      ...FollowOnlyReferenceModuleSettings\n      __typename\n    }\n    ... on DegreesOfSeparationReferenceModuleSettings {\n      ...DegreesOfSeparationReferenceModuleSettings\n      __typename\n    }\n    ... on UnknownReferenceModuleSettings {\n      ...UnknownReferenceModuleSettings\n      __typename\n    }\n    __typename\n  }\n  createdAt\n  hidden\n  isGated\n  reaction(request: {profileId: $observerId})\n  hasCollectedByMe\n  canComment(profileId: $observerId) {\n    result\n    __typename\n  }\n  canMirror(profileId: $observerId) {\n    result\n    __typename\n  }\n  canObserverDecrypt: canDecrypt(profileId: $observerId) {\n    result\n    reasons\n    __typename\n  }\n  mirrors(by: $observerId)\n  notInterested(by: $observerId)\n  bookmarked(by: $observerId)\n}\n\nfragment MirrorBase on Mirror {\n  __typename\n  id\n  createdAt\n  profile {\n    ...Profile\n    __typename\n  }\n  hidden\n}\n\nfragment ElectedMirror on ElectedMirror {\n  __typename\n  mirrorId\n  profile {\n    ...Profile\n    __typename\n  }\n  timestamp\n}\n\nfragment MirrorEvent on MirrorEvent {\n  __typename\n  profile {\n    ...Profile\n    __typename\n  }\n  timestamp\n}\n\nfragment CollectedEvent on CollectedEvent {\n  __typename\n  profile {\n    ...Profile\n    __typename\n  }\n  timestamp\n}\n\nfragment ReactionEvent on ReactionEvent {\n  __typename\n  profile {\n    ...Profile\n    __typename\n  }\n  reaction\n  timestamp\n}\n\nfragment PaginatedResultInfo on PaginatedResultInfo {\n  __typename\n  prev\n  next\n  totalCount\n}"}
cesarenaldi commented 1 year ago

@moesalih on your previous message... regarding:

i know for a fact that there is a new item in the feed

how do you know? are you using another profile?

regarding:

I just noticed that the request the sdk is sending to update beforeCount is exactly the same as the request when prev is called.

this is to be expected with current version of the Lens API.

We will be able to optimise this further once Lens API v2 lands and some of the pagination API issues are going to be solved.

I assume the second response has items: [], prev: null , next: null, can you please check for me?

moesalih commented 1 year ago

@moesalih on your previous message... regarding:

i know for a fact that there is a new item in the feed

how do you know? are you using another profile?

i'm just using my personal profile. i have the feed open on the web app which is using the sdk, and the feed open as well on the mobile app. i refresh the mobile app to see new items, then on the web go to another page, then go back to home feed and the beforeCount is not increased. this only happens sometimes so there's some inconsistent behavior.

regarding:

I just noticed that the request the sdk is sending to update beforeCount is exactly the same as the request when prev is called.

this is to be expected with current version of the Lens API.

We will be able to optimise this further once Lens API v2 lands and some of the pagination API issues are going to be solved.

I assume the second response has items: [], prev: null , next: null, can you please check for me?

yes, you're correct. the second response returns no items as you said, but the first request is actually returning the new posts that prev is supposed to show. so seems like the data is there but the sdk is not handling the state correctly.

cesarenaldi commented 1 year ago

Regarding the beforeCount not being increased when you expect it to... the probing request bypass the cache completely so wondering if this is concurrency issue on the Lens API replicas where one instance is not update yet. If you wait a little more... are you able to see the beforeCount being increased?

Maybe this is a no-issue in normal circumstances as user don't have expectation when activities are going to land on their feed.

On the issue with the second request not returning data. This is actually not an SDK issue. Given the same request params the API should idempotently return the same results set. Instead in the second request it looks like the data set got tainted by the first request and no longer returned. The PR I pointed to in my previous message is using the LensClient (which uses a cache-less GQL client) to replicate the same exact issue.

Also apparently this affects only the feed query. In my test with the publications query the prev call yields the correct results.

moesalih commented 1 year ago

I see. So what's the solution to show new content when the useFeed hook mounts? Is it possible to disable the cache on a query basis to avoid these issues?

moesalih commented 1 year ago

Regarding the beforeCount not being increased when you expect it to... the probing request bypass the cache completely so wondering if this is concurrency issue on the Lens API replicas where one instance is not update yet. If you wait a little more... are you able to see the beforeCount being increased?

I dont think this is the case. I saw an update on the feed on the mobile app and checked the website, beforeCount wasn't increased, waited a while like 5 mins, and beforeCount still not increased, checked in another tab and the feed returns latest item correctly, but the existing useFeed hook still doesn't have beforeCount increased. so there is some issue on the sdk side.

cesarenaldi commented 1 year ago

On this second issue with beforeCount not counting as expected... in your testing strategy, could you please verify the network request (and response) for the "probe request" (the request that fires as soon as the useFeed is remounted)?

I think this could well be the same bug. The new result gets "tainted" by the successful request that you do on the other tab/app.

If this is the case it will inform us on where the pagination issue lies on the server side.

Coming back to the primary issue. I don't think we have bandwidth to work on the underlying Lens API pagination issue now that we are in the thick of the Lens API v2 work. So I will need to provide a work around with a flag.

It's not like bypassing completely the cache, but sorts the same effect you expect.

moesalih commented 1 year ago

On this second issue with beforeCount not counting as expected... in your testing strategy, could you please verify the network request (and response) for the "probe request" (the request that fires as soon as the useFeed is remounted)?

I think this could well be the same bug. The new result gets "tainted" by the successful request that you do on the other tab/app.

yeah i'm sure i'm looking at the probe request. i dont think the other requests from other devices/tabs are tainting the result because those other request are fresh requests to the feed endpoint with no cursor provided so they just return everything including the latest, while your probe request includes the prev cursor.

cesarenaldi commented 1 year ago

@moesalih sorry I wasn't clear in my request... can you please copy over here the request and response of the the request that fires as soon as the useFeed is remounted (the probe request)? So to know better about this anomaly.

moesalih commented 1 year ago

@cesarenaldi yup, here's the probe request:

{"operationName":"Feed","variables":{"mediaTransformPublicationSmall":{"width":"350px","height":"auto","keepAspectRatio":true},"mediaTransformPublicationMedium":{"width":"700px","height":"auto","keepAspectRatio":true},"mediaTransformProfileThumbnail":{"width":"256px","height":"auto","keepAspectRatio":true},"metadata":{},"restrictEventTypesTo":["POST","COMMENT","MIRROR","COLLECT_POST","COLLECT_COMMENT"],"profileId":"0x2b9a","observerId":"0x2b9a","limit":50,"sources":[],"cursor":"{\"entityIdentifier\":\"\",\"timestamp\":1692284554,\"filter\":\"{\\\"mainContentFocus\\\":[\\\"ARTICLE\\\",\\\"AUDIO\\\",\\\"VIDEO\\\",\\\"EMBED\\\",\\\"IMAGE\\\",\\\"LINK\\\",\\\"TEXT_ONLY\\\"]}\",\"cursorDirection\":\"BEFORE\"}"},"query":"query Feed($profileId: ProfileId!, $restrictEventTypesTo: [FeedEventItemType!], $observerId: ProfileId, $limit: LimitScalar!, $cursor: Cursor, $sources: [Sources!]!, $metadata: PublicationMetadataFilters, $mediaTransformPublicationSmall: MediaTransformParams = {}, $mediaTransformPublicationMedium: MediaTransformParams = {}, $mediaTransformProfileThumbnail: MediaTransformParams = {}) {\n  result: feed(\n    request: {profileId: $profileId, feedEventItemTypes: $restrictEventTypesTo, limit: $limit, cursor: $cursor, sources: $sources, metadata: $metadata}\n  ) {\n    items {\n      ...FeedItem\n      __typename\n    }\n    pageInfo {\n      ...PaginatedResultInfo\n      __typename\n    }\n    __typename\n  }\n}\n\nfragment FeedItem on FeedItem {\n  __typename\n  root {\n    ... on Post {\n      ...Post\n      __typename\n    }\n    ... on Comment {\n      ...Comment\n      __typename\n    }\n    __typename\n  }\n  comments {\n    ...Comment\n    __typename\n  }\n  electedMirror {\n    ...ElectedMirror\n    __typename\n  }\n  mirrors {\n    ...MirrorEvent\n    __typename\n  }\n  collects {\n    ...CollectedEvent\n    __typename\n  }\n  reactions {\n    ...ReactionEvent\n    __typename\n  }\n}\n\nfragment Post on Post {\n  __typename\n  id\n  appId\n  stats {\n    ...PublicationStats\n    __typename\n  }\n  metadata {\n    ...MetadataOutput\n    __typename\n  }\n  profile {\n    ...Profile\n    __typename\n  }\n  collectedBy {\n    ...Wallet\n    __typename\n  }\n  collectModule {\n    ... on AaveFeeCollectModuleSettings {\n      ...AaveFeeCollectModuleSettings\n      __typename\n    }\n    ... on ERC4626FeeCollectModuleSettings {\n      ...Erc4626FeeCollectModuleSettings\n      __typename\n    }\n    ... on MultirecipientFeeCollectModuleSettings {\n      ...MultirecipientFeeCollectModuleSettings\n      __typename\n    }\n    ... on UnknownCollectModuleSettings {\n      ...UnknownCollectModuleSettings\n      __typename\n    }\n    ... on FreeCollectModuleSettings {\n      ...FreeCollectModuleSettings\n      __typename\n    }\n    ... on FeeCollectModuleSettings {\n      ...FeeCollectModuleSettings\n      __typename\n    }\n    ... on LimitedFeeCollectModuleSettings {\n      ...LimitedFeeCollectModuleSettings\n      __typename\n    }\n    ... on LimitedTimedFeeCollectModuleSettings {\n      ...LimitedTimedFeeCollectModuleSettings\n      __typename\n    }\n    ... on RevertCollectModuleSettings {\n      ...RevertCollectModuleSettings\n      __typename\n    }\n    ... on TimedFeeCollectModuleSettings {\n      ...TimedFeeCollectModuleSettings\n      __typename\n    }\n    ... on SimpleCollectModuleSettings {\n      ...SimpleCollectModuleSettings\n      __typename\n    }\n    __typename\n  }\n  collectNftAddress\n  referenceModule {\n    ... on FollowOnlyReferenceModuleSettings {\n      ...FollowOnlyReferenceModuleSettings\n      __typename\n    }\n    ... on DegreesOfSeparationReferenceModuleSettings {\n      ...DegreesOfSeparationReferenceModuleSettings\n      __typename\n    }\n    ... on UnknownReferenceModuleSettings {\n      ...UnknownReferenceModuleSettings\n      __typename\n    }\n    __typename\n  }\n  createdAt\n  hidden\n  isGated\n  reaction(request: {profileId: $observerId})\n  hasCollectedByMe\n  canComment(profileId: $observerId) {\n    result\n    __typename\n  }\n  canMirror(profileId: $observerId) {\n    result\n    __typename\n  }\n  canObserverDecrypt: canDecrypt(profileId: $observerId) {\n    result\n    reasons\n    __typename\n  }\n  mirrors(by: $observerId)\n  notInterested(by: $observerId)\n  bookmarked(by: $observerId)\n}\n\nfragment PublicationStats on PublicationStats {\n  __typename\n  totalAmountOfMirrors\n  totalUpvotes\n  totalDownvotes\n  totalAmountOfCollects\n  totalAmountOfComments\n  totalBookmarks\n  commentsCount: commentsTotal(forSources: $sources)\n}\n\nfragment MetadataOutput on MetadataOutput {\n  __typename\n  animatedUrl\n  content\n  contentWarning\n  description\n  image\n  locale\n  mainContentFocus\n  name\n  media {\n    ...PublicationMediaSet\n    __typename\n  }\n  attributes {\n    ...MetadataAttributeOutput\n    __typename\n  }\n  encryptionParams {\n    ...EncryptionParamsOutput\n    __typename\n  }\n  tags\n}\n\nfragment PublicationMediaSet on MediaSet {\n  __typename\n  original {\n    ...Media\n    __typename\n  }\n  optimized {\n    ...Media\n    __typename\n  }\n  small: transformed(params: $mediaTransformPublicationSmall) {\n    ...Media\n    __typename\n  }\n  medium: transformed(params: $mediaTransformPublicationMedium) {\n    ...Media\n    __typename\n  }\n}\n\nfragment Media on Media {\n  __typename\n  altTag\n  cover\n  mimeType\n  url\n}\n\nfragment MetadataAttributeOutput on MetadataAttributeOutput {\n  __typename\n  traitType\n  value\n}\n\nfragment EncryptionParamsOutput on EncryptionParamsOutput {\n  __typename\n  accessCondition {\n    ...RootConditionOutput\n    __typename\n  }\n  encryptionProvider\n  encryptedFields {\n    ...EncryptedFieldsOutput\n    __typename\n  }\n  providerSpecificParams {\n    encryptionKey\n    __typename\n  }\n}\n\nfragment RootConditionOutput on AccessConditionOutput {\n  __typename\n  or {\n    criteria {\n      ...AnyConditionOutput\n      __typename\n    }\n    __typename\n  }\n}\n\nfragment AnyConditionOutput on AccessConditionOutput {\n  __typename\n  ...LeafConditionOutput\n  or {\n    ...OrConditionOutput\n    __typename\n  }\n  and {\n    ...AndConditionOutput\n    __typename\n  }\n}\n\nfragment LeafConditionOutput on AccessConditionOutput {\n  __typename\n  nft {\n    ...NftOwnershipOutput\n    __typename\n  }\n  token {\n    ...Erc20OwnershipOutput\n    __typename\n  }\n  eoa {\n    ...EoaOwnershipOutput\n    __typename\n  }\n  profile {\n    ...ProfileOwnershipOutput\n    __typename\n  }\n  follow {\n    ...FollowConditionOutput\n    __typename\n  }\n  collect {\n    ...CollectConditionOutput\n    __typename\n  }\n}\n\nfragment NftOwnershipOutput on NftOwnershipOutput {\n  __typename\n  contractAddress\n  chainID\n  contractType\n  tokenIds\n}\n\nfragment Erc20OwnershipOutput on Erc20OwnershipOutput {\n  __typename\n  amount\n  chainID\n  condition\n  contractAddress\n  decimals\n  name\n  symbol\n}\n\nfragment EoaOwnershipOutput on EoaOwnershipOutput {\n  __typename\n  address\n}\n\nfragment ProfileOwnershipOutput on ProfileOwnershipOutput {\n  __typename\n  profileId\n}\n\nfragment FollowConditionOutput on FollowConditionOutput {\n  __typename\n  profileId\n}\n\nfragment CollectConditionOutput on CollectConditionOutput {\n  __typename\n  publicationId\n  thisPublication\n}\n\nfragment OrConditionOutput on OrConditionOutput {\n  __typename\n  criteria {\n    ...LeafConditionOutput\n    __typename\n  }\n}\n\nfragment AndConditionOutput on AndConditionOutput {\n  __typename\n  criteria {\n    ...LeafConditionOutput\n    __typename\n  }\n}\n\nfragment EncryptedFieldsOutput on EncryptedFieldsOutput {\n  __typename\n  animation_url\n  content\n  external_url\n  image\n  media {\n    original {\n      ...EncryptedMedia\n      __typename\n    }\n    __typename\n  }\n}\n\nfragment EncryptedMedia on EncryptedMedia {\n  __typename\n  altTag\n  cover\n  mimeType\n  url\n}\n\nfragment Profile on Profile {\n  ...ProfileFields\n  invitedBy {\n    ...ProfileFields\n    __typename\n  }\n  __typename\n}\n\nfragment ProfileFields on Profile {\n  __typename\n  id\n  name\n  bio\n  handle\n  ownedBy\n  interests\n  followNftAddress\n  picture {\n    ... on NftImage {\n      ...NftImage\n      __typename\n    }\n    ... on MediaSet {\n      ...ProfilePictureSet\n      __typename\n    }\n    __typename\n  }\n  coverPicture {\n    ... on NftImage {\n      ...NftImage\n      __typename\n    }\n    ... on MediaSet {\n      ...ProfileCoverSet\n      __typename\n    }\n    __typename\n  }\n  stats {\n    ...ProfileStats\n    __typename\n  }\n  followModule {\n    ... on FeeFollowModuleSettings {\n      ...FeeFollowModuleSettings\n      __typename\n    }\n    ... on ProfileFollowModuleSettings {\n      ...ProfileFollowModuleSettings\n      __typename\n    }\n    ... on RevertFollowModuleSettings {\n      ...RevertFollowModuleSettings\n      __typename\n    }\n    ... on UnknownFollowModuleSettings {\n      ...UnknownFollowModuleSettings\n      __typename\n    }\n    __typename\n  }\n  __attributes: attributes {\n    ...Attribute\n    __typename\n  }\n  dispatcher {\n    address\n    canUseRelay\n    __typename\n  }\n  onChainIdentity {\n    proofOfHumanity\n    ens {\n      name\n      __typename\n    }\n    sybilDotOrg {\n      verified\n      source {\n        twitter {\n          handle\n          __typename\n        }\n        __typename\n      }\n      __typename\n    }\n    worldcoin {\n      isHuman\n      __typename\n    }\n    __typename\n  }\n  isFollowedByMe\n  isFollowingObserver: isFollowing(who: $observerId)\n}\n\nfragment NftImage on NftImage {\n  __typename\n  contractAddress\n  tokenId\n  uri\n  verified\n}\n\nfragment ProfilePictureSet on MediaSet {\n  __typename\n  original {\n    ...Media\n    __typename\n  }\n  optimized {\n    ...Media\n    __typename\n  }\n  thumbnail: transformed(params: $mediaTransformProfileThumbnail) {\n    ...Media\n    __typename\n  }\n}\n\nfragment ProfileCoverSet on MediaSet {\n  __typename\n  original {\n    ...Media\n    __typename\n  }\n  optimized {\n    ...Media\n    __typename\n  }\n}\n\nfragment ProfileStats on ProfileStats {\n  __typename\n  totalCollects\n  totalComments\n  totalFollowers\n  totalFollowing\n  totalMirrors\n  totalPosts\n  totalPublications\n  commentsCount: commentsTotal(forSources: $sources)\n  postsCount: postsTotal(forSources: $sources)\n  mirrorsCount: mirrorsTotal(forSources: $sources)\n}\n\nfragment FeeFollowModuleSettings on FeeFollowModuleSettings {\n  __typename\n  amount {\n    ...ModuleFeeAmount\n    __typename\n  }\n  contractAddress\n  recipient\n}\n\nfragment ModuleFeeAmount on ModuleFeeAmount {\n  __typename\n  asset {\n    ...Erc20Fields\n    __typename\n  }\n  value\n}\n\nfragment Erc20Fields on Erc20 {\n  __typename\n  name\n  symbol\n  decimals\n  address\n}\n\nfragment ProfileFollowModuleSettings on ProfileFollowModuleSettings {\n  __typename\n  contractAddress\n}\n\nfragment RevertFollowModuleSettings on RevertFollowModuleSettings {\n  __typename\n  contractAddress\n}\n\nfragment UnknownFollowModuleSettings on UnknownFollowModuleSettings {\n  __typename\n  contractAddress\n}\n\nfragment Attribute on Attribute {\n  __typename\n  displayType\n  key\n  value\n}\n\nfragment Wallet on Wallet {\n  __typename\n  address\n  defaultProfile {\n    ...Profile\n    __typename\n  }\n}\n\nfragment AaveFeeCollectModuleSettings on AaveFeeCollectModuleSettings {\n  __typename\n  amount {\n    ...ModuleFeeAmount\n    __typename\n  }\n  collectLimitOptional: collectLimit\n  contractAddress\n  followerOnly\n  endTimestampOptional: endTimestamp\n  recipient\n  referralFee\n}\n\nfragment Erc4626FeeCollectModuleSettings on ERC4626FeeCollectModuleSettings {\n  __typename\n  amount {\n    ...ModuleFeeAmount\n    __typename\n  }\n  collectLimitOptional: collectLimit\n  contractAddress\n  followerOnly\n  endTimestampOptional: endTimestamp\n  recipient\n  referralFee\n  vault\n}\n\nfragment MultirecipientFeeCollectModuleSettings on MultirecipientFeeCollectModuleSettings {\n  __typename\n  amount {\n    ...ModuleFeeAmount\n    __typename\n  }\n  collectLimitOptional: collectLimit\n  contractAddress\n  followerOnly\n  endTimestampOptional: endTimestamp\n  recipients {\n    recipient\n    split\n    __typename\n  }\n  referralFee\n}\n\nfragment UnknownCollectModuleSettings on UnknownCollectModuleSettings {\n  __typename\n  contractAddress\n  collectModuleReturnData\n}\n\nfragment FreeCollectModuleSettings on FreeCollectModuleSettings {\n  __typename\n  contractAddress\n  followerOnly\n}\n\nfragment FeeCollectModuleSettings on FeeCollectModuleSettings {\n  __typename\n  amount {\n    ...ModuleFeeAmount\n    __typename\n  }\n  contractAddress\n  followerOnly\n  recipient\n  referralFee\n}\n\nfragment LimitedFeeCollectModuleSettings on LimitedFeeCollectModuleSettings {\n  __typename\n  amount {\n    ...ModuleFeeAmount\n    __typename\n  }\n  collectLimit\n  contractAddress\n  followerOnly\n  recipient\n  referralFee\n}\n\nfragment LimitedTimedFeeCollectModuleSettings on LimitedTimedFeeCollectModuleSettings {\n  __typename\n  amount {\n    ...ModuleFeeAmount\n    __typename\n  }\n  collectLimit\n  contractAddress\n  followerOnly\n  endTimestamp\n  recipient\n  referralFee\n}\n\nfragment RevertCollectModuleSettings on RevertCollectModuleSettings {\n  __typename\n  contractAddress\n}\n\nfragment TimedFeeCollectModuleSettings on TimedFeeCollectModuleSettings {\n  __typename\n  amount {\n    ...ModuleFeeAmount\n    __typename\n  }\n  contractAddress\n  followerOnly\n  endTimestamp\n  recipient\n  referralFee\n}\n\nfragment SimpleCollectModuleSettings on SimpleCollectModuleSettings {\n  __typename\n  contractAddress\n  followerOnly\n  feeOptional: fee {\n    amount {\n      ...ModuleFeeAmount\n      __typename\n    }\n    referralFee\n    recipient\n    __typename\n  }\n  collectLimitOptional: collectLimit\n  endTimestampOptional: endTimestamp\n}\n\nfragment FollowOnlyReferenceModuleSettings on FollowOnlyReferenceModuleSettings {\n  __typename\n  contractAddress\n}\n\nfragment DegreesOfSeparationReferenceModuleSettings on DegreesOfSeparationReferenceModuleSettings {\n  __typename\n  contractAddress\n  commentsRestricted\n  degreesOfSeparation\n  mirrorsRestricted\n}\n\nfragment UnknownReferenceModuleSettings on UnknownReferenceModuleSettings {\n  __typename\n  contractAddress\n  referenceModuleReturnData\n}\n\nfragment Comment on Comment {\n  __typename\n  ...CommentBase\n  commentOn {\n    ... on Post {\n      ...Post\n      __typename\n    }\n    ... on Mirror {\n      ...MirrorBase\n      __typename\n    }\n    ... on Comment {\n      ...CommentBase\n      __typename\n    }\n    __typename\n  }\n  mainPost {\n    ... on Post {\n      ...Post\n      __typename\n    }\n    ... on Mirror {\n      ...MirrorBase\n      __typename\n    }\n    __typename\n  }\n  firstComment {\n    ...CommentBase\n    __typename\n  }\n}\n\nfragment CommentBase on Comment {\n  __typename\n  id\n  appId\n  stats {\n    ...PublicationStats\n    __typename\n  }\n  metadata {\n    ...MetadataOutput\n    __typename\n  }\n  profile {\n    ...Profile\n    __typename\n  }\n  collectedBy {\n    ...Wallet\n    __typename\n  }\n  collectModule {\n    ... on AaveFeeCollectModuleSettings {\n      ...AaveFeeCollectModuleSettings\n      __typename\n    }\n    ... on ERC4626FeeCollectModuleSettings {\n      ...Erc4626FeeCollectModuleSettings\n      __typename\n    }\n    ... on MultirecipientFeeCollectModuleSettings {\n      ...MultirecipientFeeCollectModuleSettings\n      __typename\n    }\n    ... on UnknownCollectModuleSettings {\n      ...UnknownCollectModuleSettings\n      __typename\n    }\n    ... on FreeCollectModuleSettings {\n      ...FreeCollectModuleSettings\n      __typename\n    }\n    ... on FeeCollectModuleSettings {\n      ...FeeCollectModuleSettings\n      __typename\n    }\n    ... on LimitedFeeCollectModuleSettings {\n      ...LimitedFeeCollectModuleSettings\n      __typename\n    }\n    ... on LimitedTimedFeeCollectModuleSettings {\n      ...LimitedTimedFeeCollectModuleSettings\n      __typename\n    }\n    ... on RevertCollectModuleSettings {\n      ...RevertCollectModuleSettings\n      __typename\n    }\n    ... on TimedFeeCollectModuleSettings {\n      ...TimedFeeCollectModuleSettings\n      __typename\n    }\n    ... on SimpleCollectModuleSettings {\n      ...SimpleCollectModuleSettings\n      __typename\n    }\n    __typename\n  }\n  collectNftAddress\n  referenceModule {\n    ... on FollowOnlyReferenceModuleSettings {\n      ...FollowOnlyReferenceModuleSettings\n      __typename\n    }\n    ... on DegreesOfSeparationReferenceModuleSettings {\n      ...DegreesOfSeparationReferenceModuleSettings\n      __typename\n    }\n    ... on UnknownReferenceModuleSettings {\n      ...UnknownReferenceModuleSettings\n      __typename\n    }\n    __typename\n  }\n  createdAt\n  hidden\n  isGated\n  reaction(request: {profileId: $observerId})\n  hasCollectedByMe\n  canComment(profileId: $observerId) {\n    result\n    __typename\n  }\n  canMirror(profileId: $observerId) {\n    result\n    __typename\n  }\n  canObserverDecrypt: canDecrypt(profileId: $observerId) {\n    result\n    reasons\n    __typename\n  }\n  mirrors(by: $observerId)\n  notInterested(by: $observerId)\n  bookmarked(by: $observerId)\n}\n\nfragment MirrorBase on Mirror {\n  __typename\n  id\n  createdAt\n  profile {\n    ...Profile\n    __typename\n  }\n  hidden\n}\n\nfragment ElectedMirror on ElectedMirror {\n  __typename\n  mirrorId\n  profile {\n    ...Profile\n    __typename\n  }\n  timestamp\n}\n\nfragment MirrorEvent on MirrorEvent {\n  __typename\n  profile {\n    ...Profile\n    __typename\n  }\n  timestamp\n}\n\nfragment CollectedEvent on CollectedEvent {\n  __typename\n  profile {\n    ...Profile\n    __typename\n  }\n  timestamp\n}\n\nfragment ReactionEvent on ReactionEvent {\n  __typename\n  profile {\n    ...Profile\n    __typename\n  }\n  reaction\n  timestamp\n}\n\nfragment PaginatedResultInfo on PaginatedResultInfo {\n  __typename\n  prev\n  next\n  totalCount\n}"}

response:

{
    "data": {
        "result": {
            "items": [],
            "pageInfo": {
                "__typename": "PaginatedResultInfo",
                "prev": null,
                "next": null,
                "totalCount": null
            },
            "__typename": "PaginatedFeedResult"
        }
    }
}
cesarenaldi commented 1 year ago

Hey @moesalih thank you for taking the time. Yeah, I can confirm this is the same Lens API bug. Interesting that even a request from another thread without a cursor at all is able to compromise the subsequent request with a cursor (the probe request in this case but could have been a manual "prev" request).

This give us more information to narrow down the issue on the API side.

cesarenaldi commented 7 months ago

Checking in on this issue. We are not aware of issue with this adopting the beforeCount in v2 version of the SDK.

cesarenaldi commented 7 months ago

Closing this issue as stale. Feel free to reopen if you experience the issue with Lens SDK v2.