10. लेंस प्रोटोकॉल के साथ विकेंद्रीकृत(decentralised) ट्विटर कैसे बनाएं?

फेसबुक और ट्विटर जैसे सोशल मीडिया दिग्गजों के पास कितना नियंत्रण है, इस बारे में हाल ही में काफी चर्चा हुई है। इस पर आपकी राय चाहे जो भी हो, हम आपके साथ एक प्रयोग साझा करना चाहते हैं जो बेहद रोमांचक है।

इस साल, Aave टीम के लोगों ने Lens Protocol(लेंस प्रोटोकॉल) नामक एक प्रोजेक्ट लॉन्च किया। यह हाल ही में वेब3 पारिस्थितिकी तंत्र(ecosystem) में प्रवेश करने वाली सबसे रोमांचक तकनीकों में से एक है क्योंकि यह ब्लॉकचेन टेक्नोलॉजी का उपयोग करता है डेटा की शक्ति और नियंत्रण उपयोगकर्ताओं को वापस देने के लिए जो इसे उत्पन्न करते हैं।

तो हमने सोचा, क्यों न हम सब मिलकर इसे एक्स्प्लोर करें? अब तक आपने जो कुछ भी सीखा है उन सब को जोड़ना एक उचित अंतिम पाठ होगा 🙂।

इस पाठ में आप सीखेंगे:

  • Apollo GraphQL क्लाइंट के साथ Next.js ऐप कैसे सेट अप करें
  • पोलीगोन ब्लॉकचैन पर संग्रहीत प्रोफाइल, पोस्ट और अन्य डेटा लाने के लिए लेंस प्रोटोकॉल एपीआई का उपयोग कैसे करें
  • MintKudos/मिंटकुडोस एपीआई का परिचय -- ताकि आप अपने पीओके(PoK - Proof of Knowledge) टोकन को अपने डीऐप में एकीकृत/Integrate कर सकें!
  • Lit Protocol/लिट प्रोटोकॉल का एक परिचय -- यदि आप कुछ पोस्ट को केवल विभिन्न समुदाय के सदस्यों को दिखाने के लिए एन्क्रिप्ट/encrypt करना चाहते हैं
  • Repl.it का उपयोग करके अपने विकेंद्रीकृत सोशल मीडिया ऐप फ्रंटएंड वेबसाइट को कैसे परिनियोजित/deploy करें
  • इस परियोजना का विस्तार करने के लिए कई चुनौती विकल्प!

यहां एक GitHub रेपो है जिसमें तैयार परियोजना का एक उदाहरण है यदि आपको साथ चलते समय उसकी ज़रूरत हो

चलो शुरू करें!

2048

(वीडियो संस्करण जल्द ही आ रहा है!)

चरण 0: लेंस प्रोटोकॉल पारिस्थितिकी तंत्र का भ्रमण करें

2000

https://lens.xyz/

आइए एक नजर डालते हुए देखें कि लेंस प्रोटोकॉल क्या पेश(offer) करता है!

यदि आप लेंस वेबसाइट पर जाते हैं, तो आपको तुरंत दो लिंक दिखाई देंगे।

  • Developer Garder - आपको डॉक्यूमेंटेशन एंड गाइड्स पर ले जाता है
  • Join Discord - आपको कम्युनिटी चैट सर्वर पर ले जाता है

हम थोड़ी देर में डेवलपर गार्डन का दौरा करेंगे, लेकिन इससे पहले हम सबसे ऊपर दाईं ओर मेनू बार विकल्प देखते हैं।

639

आप उन ऐप्स को देख सकते हैं जो पहले से ही कम्युनिटी द्वारा बनाए जा रहे हैं!

यहाँ, हम आपका ध्यान दो बातों की ओर आकर्षित करेंगे:

  1. Claim Handle(क्लेम हैंडल) - अभी (7/13/2022) तक, लेंस प्रोटोकॉल अभी भी ओपन बीटा(open beta) में है, इसलिए आपको अपने वॉलेट पर एक हैंडल क्लेम करने के लिए अनुमति प्राप्त करने की आवश्यकता होगी। सौभाग्य से, यदि आप रोड टू वेब3 सामुदायिक कार्यक्रम का पालन कर रहे हैं और अतीत में प्रूफ ऑफ नॉलेज एनएफटी अर्जित कर चुके हैं, तो आपको पहले से ही अनुमति मिली होनी चाहिए! यदि ऐसा नहीं है, तो कृपया अल्केमी डिस्कॉर्ड में आएं और #week-10 चैनल में इसके बारे में पूछें। एक बार जब आप अपना हैंडल प्राप्त कर लेते हैं, तो लेंस्टर पर रोड टू वेब3 समुदाय(community) को देखें 🔥
  2. Apps - यह लिंक आपको समुदाय-निर्मित वेब एप्लिकेशन की एक सूची पर ले जाता है, जिसमें Lensfrens, Lenster, Phaver, Alps Finance, Refract, और बहुत कुछ शामिल हैं।

मैं लेंस प्रोटोकॉल की शक्ति को दर्शाने के लिए एक त्वरित क्षण लेना चाहता हूं।

अगर आप LensFrens(लेंसफ्रेंस) पर इस प्रोफाइल पेज पर जाते हैं - https://www.lensfrens.xyz/thatguyintech.lens - आपको मेरा अकाउंट दिखाई देगा, जो कुछ इस तरह दिखना चाहिए:

1278

lensfrens

अब यदि आप इस पूरी तरह से अलग वेब एप्लिकेशन Lenster(लेनस्टर) - https://lenster.xyz/u/thatguyintech.lens पर जाते हैं - आप मुझे फिर से देखेंगे, ठीक उसी प्रोफ़ाइल डेटा के साथ, यहां तक कि पोस्ट/टिप्पणियों सहित/अन्य प्रोफाइल से प्रतिक्रियाएं भी वही रहेंग, केवल एक पूरी तरह से अलग अनुभव को छोड़कर।

1281

लेन्स्टर

अब तक कुछ भी सुपर माइंड-ब्लोइंग नहीं है, लेकिन यहां हमारे साथ बने रहें।

अन्य ऐप्स देखें:

  • Phaver/फेवर - लेंस सपोर्ट वाला सोशल मोबाइल ऐप जो आपको अन्य लोगों के पोस्ट को क्यूरेट करने के लिए "स्टेक/stake" करने देता है, जिससे लोग अपने कंटेंट के लिए पैसे कमा सकते हैं।
1170
  • Refract/रिफ्रेक्ट - यह हैकर न्यूज की तरह है, सिवाय इसके कि यहां साझा किए जाने वाले सभी लिंक और पोस्ट लेंस प्रोटोकॉल द्वारा संचालित(powered) हैं।
2000

Refract

ये ऐप्स अलग-अलग लोगों, अलग-अलग टीमों द्वारा तथा अलग-अलग उपयोगकर्ता अनुभव और उत्पाद/प्रोडक्ट लक्ष्यों के साथ बनाए गए हैं, लेकिन सारा अंतर्निहित(underlying) डेटा समान हैं, जैसे कि वे सभी एक ही डेटाबेस और एपीआईज साझा करते हैं।

यह कैसे हो सकता है?

पता चला है कि एक साझा/shared डेटाबेस और सार्वजनिक एपीआई वास्तव में लेंस प्रोटोकॉल के fundamental ideas(मौलिक विचार) हैं। इसलिए इस तकनीक में इतनी संभावनाएं(potential) हैं।

डेटा का हर भाग/टुकड़ा एक NFT है।

हर पोस्ट, हर कमेंट, हर प्रतिक्रिया, हर फॉलो। डेटा के इन टुकड़ों में से प्रत्येक को एक non-fungible token(अपूरणीय टोकन) के रूप में संग्रहीत किया जाता है तथा आपके, यानि निर्माता द्वारा बनाया और नियंत्रित किया जाता है।

इसका मतलब यह है कि हम उपयोगकर्ताओं के रूप में जो डिजिटल कंटेंट और संबंध(relationships) बनाते हैं, वह हम ओन करते हैं(owned by us) और इसे प्रोटोकॉल के ऊपर बने किसी भी एप्लिकेशन में ले जाया जा सकता है!

चलो शुरू करें!

चरण 1. एक Next.js एप्लिकेशन सेट अप करें और Apollo/अपोलो इंस्टॉल करें

कमांड लाइन खोलें। एक प्रोजेक्ट शुरू करने के लिए create-next-app का उपयोग करें जिसे road-to-lens नाम दिया जाएगा

npx create-next-app road-to-lens
2000

उत्पन्न रेपो को road-to-lens कहा जाएगा, और आपके पास निम्नलिखित डाइरेक्टरी(directory) स्ट्रक्चर होना चाहिए:

thatguyintech@albert road-to-lens % tree -L 1
.
├── README.md
├── next.config.js
├── node_modules
├── package.json
├── pages
├── public
├── styles
└── yarn.lock

जब तक हम यहां हैं, हम अपना graphql client(ग्राफक्यूएल क्लाइंट) भी इंस्टॉल कर लेते हैं। हम डेटा के लिए लेंस प्रोटोकॉल को क्वेरी करने के लिए Apollo का उपयोग करेंगे।

npm install @apollo/client graphql

इसके पूरा होने के बाद, हम एक स्थानीय सर्वर शुरू करके और वेबपेज को लोड करके एक विवेक जांच(sanity check) कर सकते हैं:

thatguyintech@albert road-to-lens % npm run dev

> [email protected] dev
> next dev

ready - started server on 0.0.0.0:3000, url: http://localhost:3000
info  - SWC minify release candidate enabled. https://nextjs.link/swcmin
event - compiled client and server successfully in 4.2s (169 modules)

लोड होने के बाद http://localhost:3000 को आपको एक मूल टेम्पलेट पेज देना चाहिए जो इस तरह दिखता है:

2000

चरण 2: अपोलो ग्राफक्यूएल को लेंस से अनुशंसित(recommended) प्रोफाइल के साथ index.js पेज पर आज़माएं

आइए होम पेज पर अनुशंसित लेंस प्रोफाइल लोड करके Apollo/अपोलो और GraphQL/ग्राफक्यूएल से परिचित हों।

सबसे पहले, अपोलो provider/प्रदाता को हमारे पूरे ऐप को wrap/रैप करने के लिए सेट करें ताकि हमारे पास बाद में useQuery और useMutation जैसे मेथड्स तक पहुंच हो।

apollo-client.js नामक शीर्ष-स्तरीय(top-level) डायरेक्टरी में एक फ़ाइल बनाएँ

thatguyintech@albert road-to-lens % touch apollo-client.js

हम लेंस मैटिक मेननेट एपीआई पर इंगित बेस यूआरएल के साथ यहां एक क्लाइंट को इनिशियलाइज़ करेंगे:

// ./apollo-client.js

import { ApolloClient, InMemoryCache } from "@apollo/client";

const client = new ApolloClient({
    uri: "https://api.lens.dev",
    cache: new InMemoryCache(),
});

export default client;

इस GraphQL क्लाइंट के शुरू होने के साथ, हम इसे अपने /pages/_app.js फाइल में आयात कर सकते हैं और इसका उपयोग अपने ग्लोबल ऐप कॉम्पोनेन्ट को लपेटने के लिए कर सकते हैं:

import '../styles/globals.css'
import { ApolloProvider } from "@apollo/client";
import client from "../apollo-client";

function MyApp({ Component, pageProps }) {
  return (
    <ApolloProvider client={client}>
      <Component {...pageProps} />
    </ApolloProvider>
  );
}

export default MyApp

आप यहां देख सकते हैं कि हमने <ApolloProvider client={client}> को रैपर के रूप में जोड़ा है। यह हमारे पूरे ऐप को सुपरपॉवर देता है — हर जगह हम लेंस एपीआई से डेटा लाने और साथ ही अपडेट भेजने के लिए यूटिलिटी मेथड्स जैसे useQuery और useMutation का उपयोग करने में सक्षम होंगे।

लोकलहोस्ट पर दोबारा जांच करने से पहले एक आखिरी अपडेट। आइए लेंस से रेकमेंडेड प्रोफाइल लाने के लिए क्वेरी बनाने के लिए /pages/index.js को अपडेट करें:

import { useQuery, gql } from "@apollo/client";

const recommendProfiles = gql`
  query RecommendedProfiles {
    recommendedProfiles {
          id
        name
        bio
        attributes {
          displayType
          traitType
          key
          value
        }
          followNftAddress
        metadata
        isDefault
        picture {
          ... on NftImage {
            contractAddress
            tokenId
            uri
            verified
          }
          ... on MediaSet {
            original {
              url
              mimeType
            }
          }
          __typename
        }
        handle
        coverPicture {
          ... on NftImage {
            contractAddress
            tokenId
            uri
            verified
          }
          ... on MediaSet {
            original {
              url
              mimeType
            }
          }
          __typename
        }
        ownedBy
        dispatcher {
          address
          canUseRelay
        }
        stats {
          totalFollowers
          totalFollowing
          totalPosts
          totalComments
          totalMirrors
          totalPublications
          totalCollects
        }
        followModule {
          ... on FeeFollowModuleSettings {
            type
            amount {
              asset {
                symbol
                name
                decimals
                address
              }
              value
            }
            recipient
          }
          ... on ProfileFollowModuleSettings {
          type
          }
          ... on RevertFollowModuleSettings {
          type
          }
        }
    }
  }
`;

export default function Home() {
  const {loading, error, data} = useQuery(recommendProfiles);

  if (loading) return 'Loading..';
  if (error) return `Error! ${error.message}`;

  return (
    <div>
      Hello
      {data.recommendedProfiles.map((profile, index) => {
        console.log(`Profile ${index}:`, profile);
        return (
          <div>
            <h1>{profile.name}</h1>
            <p>{profile.bio}</p>
            <div>{profile.attributes.map((attr, idx) => {
              if (attr.key === "website") {
                return <div><a href={`${attr.value}`}>{attr.value}</a><br/></div>
              } else if (attr.key === "twitter") {
                return <div><a href={`https://twitter.com/${attr.value}`}>@{attr.value}</a><br/></div>;
              }
              return(<div>{attr.value}</div>);
            })}</div>
          </div>
        );
      })}
    </div>
  )
}

हम इस परिवर्तन के साथ कुछ प्रमुख चीज़ें कर रहे हैं:

  1. RecommendedProfiles नामक एक ग्राफक्यूएल क्वेरी को परिभाषित/define करें।
  2. RecommendedProfiles क्वेरी के साथ useQuery को कॉल करके प्रोफ़ाइल की एक सूची प्राप्त करें -> जो data वेरिएबल में वापस आती है।
  3. कुछ प्रोफ़ाइल जानकारी प्रदर्शित करें जैसे कि data.profile.name, data.profile.bio, और data.profile.attributes

http://localhost:3000/ पर वापस जाएं (सुनिश्चित करें कि आपका स्थानीय सर्वर चल रहा है), और फिर आखिरकार, आपको प्रोफाइल की यह वास्तव में सरल सूची देखनी चाहिए!

2000

चाहे देखने में अच्छा न हो लेकिन कितना बढ़िया है ना?

यदि आप यहां रुकना चाहते हैं और कुछ और एक्सप्लोरेशन करना चाहते हैं, तो आप कुछ और डेटा देखने के लिए अपनी /pages/index.js फ़ाइल में एक console.log स्टेटमेंट जोड़ सकते हैं जो कि GraphQL क्वेरी से लौटाया गया है।

उदाहरण के लिए, console.log(data) जोड़कर, आप अपने ब्राउज़र पर डेवलपर कंसोल को खोलने पर प्रोफ़ाइल डेटा देख सकेंगे।

2000

कंसोल में या डेवलपर डॉक्स पर RecommendedProfiles रिस्पांस देखें

हम इस डेटा को बाद में देखेंगे और पेज डिजाइन को साफ करेंगे 🙂

और एक अनुस्मारक: वह सारा डेटा एनएफटी के रूप में ब्लॉकचैन पर संग्रहीत है!

लेंस टीम अपने एपीआई के साथ जो कर रही है वह सिर्फ सभी ऑन-चेन डेटा को अनुक्रमित(index) कर रही है ताकि डेवलपर्स के लिए एनएफटी का उपयोग करना और निर्माण करना आसान हो!

इस पॉइंट पर एक त्वरित विवेक जांच..

आपकी डायरेक्टरी संरचना कुछ इस तरह दिखनी चाहिए:

thatguyintech@albert road-to-lens % tree -L 2
.
├── README.md
├── apollo-client.js      <- we created this
├── next.config.js
├── node_modules
├── package-lock.json
├── package.json
├── pages
│   ├── api
│   ├── _app.js           <- we modified this
│   └── index.js          <- we modified this
├── public
│   ├── favicon.ico
│   └── vercel.svg
├── styles
│   ├── Home.module.css
│   └── globals.css
└── yarn.lock

चरण 2: चलिए प्रोफाइल सूची(list) को और अच्छा बनाते हैं

इस भाग में, हम नेविगेट करना आसान बनाने के लिए अपने कोड को रिफैक्टर करेंगे, और हम अपने कंपोनेंट्स को स्टाइल करेंगे ताकि UI अधिक साफ दिखे।

रिफैक्टर से शुरू:

ये रहा हमारा नया /pages/index.js

import { useQuery } from "@apollo/client";
import recommendedProfilesQuery from '../queries/recommendedProfilesQuery.js';
import Profile from '../components/Profile.js';

export default function Home() {
  const {loading, error, data} = useQuery(recommendedProfilesQuery);

  if (loading) return 'Loading..';
  if (error) return `Error! ${error.message}`;

  return (
    <div>
      {data.recommendedProfilesQuery.map((profile, index) => {
        console.log(`Profile ${index}:`, profile);
        return <Profile key={profile.id} profile={profile} displayFullProfile={false} />;
      })}
    </div>
  )
}

हम recommendProfilesQuery को एक नए फोल्डर में एक अलग GraphQL डॉक्यूमेंट में स्थानांतरित कर रहे हैं, जिस फोल्डर की आपको queries बनाने के लिए आवश्यकता है:

mkdir queries
touch queries/recommendedProfilesQuery.js

और उसके बाद RecommendedProfiles डॉक्यूमेंट को इस फ़ाइल पर कॉपी करें:

// queries/recommendedProfilesQuery.js

import {gql} from '@apollo/client';

export default gql`
  query RecommendedProfiles {
    recommendedProfiles {
          id
        name
        bio
        attributes {
          displayType
          traitType
          key
          value
        }
          followNftAddress
        metadata
        isDefault
        picture {
          ... on NftImage {
            contractAddress
            tokenId
            uri
            verified
          }
          ... on MediaSet {
            original {
              url
              mimeType
            }
          }
          __typename
        }
        handle
        coverPicture {
          ... on NftImage {
            contractAddress
            tokenId
            uri
            verified
          }
          ... on MediaSet {
            original {
              url
              mimeType
            }
          }
          __typename
        }
        ownedBy
        dispatcher {
          address
          canUseRelay
        }
        stats {
          totalFollowers
          totalFollowing
          totalPosts
          totalComments
          totalMirrors
          totalPublications
          totalCollects
        }
        followModule {
          ... on FeeFollowModuleSettings {
            type
            amount {
              asset {
                symbol
                name
                decimals
                address
              }
              value
            }
            recipient
          }
          ... on ProfileFollowModuleSettings {
          type
          }
          ... on RevertFollowModuleSettings {
          type
          }
        }
    }
  }
`;

और फिर चलिये हम उस Profile कॉम्पोनेंट को भी बनाते हैं जिसे हमने ऊपर ( import Profile from '../components/Profile.js';) में index.js पेज में पेश किया था

हमें इसे एक नई डायरेक्टरी में व्यवस्थित(organize) करना चाहिए जिसे components कहा जाता है।

mkdir components
touch components/Profile.js

और फिर इस संरचना(structure) को इसमें कॉपी करें:और फिर इस संरचना(structure) को इसमें कॉपी करें:

// components/Profile.js

import Link from "next/link";
export default function Profile(props) {
  const profile = props.profile;

  // When displayFullProfile is true, we show more info.
  const displayFullProfile = props.displayFullProfile;

  return (
    <div className="p-8">
      <Link href={`/profile/${profile.id}`}>
        <div className="max-w-md mx-auto bg-white rounded-xl shadow-md overflow-hidden md:max-w-2xl">
          <div className="md:flex">
            <div className="md:shrink-0">
              {profile.picture ? (
                <img
                  src={
                    profile.picture.original
                      ? profile.picture.original.url
                      : profile.picture.uri
                  }
                  className="h-48 w-full object-cover md:h-full md:w-48"
                />
              ) : (
                <div
                  style={{
                    backgrondColor: "gray",
                  }}
                  className="h-48 w-full object-cover md:h-full md:w-48"
                />
              )}
            </div>
            <div className="p-8">
              <div className="uppercase tracking-wide text-sm text-indigo-500 font-semibold">
                {profile.handle}
                {displayFullProfile &&
                  profile.name &&
                  " (" + profile.name + ")"}
              </div>
              <div className="block mt-1 text-sm leading-tight font-medium text-black hover:underline">
                {profile.bio}
              </div>
              <div className="mt-2 text-sm text-slate-900">{profile.ownedBy}</div>
              <p className="mt-2 text-xs text-slate-500">
                following: {profile.stats.totalFollowing} followers:{" "}
                {profile.stats.totalFollowers}
              </p>
            </div>
          </div>
        </div>
      </Link>
    </div>
  );
}

यह Profile कॉम्पोनेंट एक इनपुट के रूप में props को स्वीकार करता है, और उम्मीद करता है कि props ऑब्जेक्ट में एक profile फील्ड हो, जिसमें वह सभी जानकारी शामिल हो, जिसे हम कॉम्पोनेंट रेंडर करते समय प्रदर्शित करना चाह सकते हैं।

जब हम यहां थे, हमने प्रोफाइल पिक्चर और फॉलोअर्स/फॉलोइंग की संख्या भी जोड़ दी!

अब, एक बार फिर से, हमारे काम की जाँच करें। अपने काम की अक्सर जाँच करना एक अच्छी आदत है क्योंकि यह हमें जल्दी से bugs(बग्स) पकड़ने में मदद करता है।

सुनिश्चित करें कि आपका सर्वर चल रहा है:

thatguyintech@albert road-to-lens % npm run dev

> [email protected] dev
> next dev

ready - started server on 0.0.0.0:3000, url: http://localhost:3000

और लोकलहोस्ट पर जाएँ: http://localhost:3000/

आपको ऐसा कुछ देखना चाहिए:

1078

यह अभी भी उतना अच्छा नहीं दिख रहा है, लेकिन हमारे पास तस्वीरें हैं!

इससे पहले कि हम इस चरण को पूरा करें, आइए उस CSS का लाभ उठाएं जिसे हमने Profile कॉम्पोनेंट में शामिल किया है।

अगर आपने पहले कभी भी Tailwind CSS का इस्तेमाल नहीं किया है, तो ये टैग:

uppercase tracking-wide text-sm text-indigo-500 font-semibold

सभी Tailwind के डिजाइन के utility-first(यूटिलिटी-फर्स्ट) फंडामेंटल से आते हैं (डॉक्यूमेंटेशन देखें)।

तो वास्तव में हमें केवल इतना करना है कि उनके इंस्टालेशन इंस्ट्रक्शंस के अनुसार टेलविंड इनस्टॉल करें:

npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p

इन कॉन्फिगस को कॉपी करें ताकि टेलविंड को पता चले कि हमारे प्रोजेक्ट में styles को लोड करने के लिए कौन से paths(पथ) हैं। हम चाहते हैं कि pages फोल्डर और components फोल्डर में सब कुछ कवर हो, इसलिए हम इन paths(पथ) का उपयोग कर रहे हैं:

// tailwind.config.js

module.exports = {
  content: [
    "./pages/**/*.{js,ts,jsx,tsx}",
    "./components/**/*.{js,ts,jsx,tsx}",
  ],
  theme: {
    extend: {},
  },
  plugins: [],
}

अब हम अपनी CSS फ़ाइल में Tailwind निर्देश(directives) जोड़कर समाप्त करेंगे:

/* ./styles/globals.css */
@tailwind base;
@tailwind components;
@tailwind utilities;

सर्वर को फिर से शुरू करें और देखें कि यह अब कैसा दिखता है!

881

Tailwind CSS के साथ स्टाइलिंग के बाद बेहतर लग रहा है

यह अद्भुत लग रहा है। यहां तक कि ड्रॉप-शैडोज और भी बहुत कुछ है 🙂

अब.. आप में से कुछ लोगों ने देखा होगा कि मैंने प्रोफ़ाइल कॉम्पोनेन्ट में कुछ <Link> शामिल किए हैं, और फिर जब आप किसी एक प्रोफ़ाइल पर क्लिक करते हैं, तो यह एक नए पृष्ठ से जुड़ जाता है!

उदाहरण के लिए, DAVIDEV.LENS के कार्ड पर क्लिक करना हमें http://localhost:3000/profile/0x16 पर ले जाता है और 404 error दिखाता है।

789

404! ओह तेरी

घबराएं नहीं, अगले चरण में इस पेज का निर्माण करते हैं और Publications/प्रकाशनों(उर्फ "posts" जैसे अन्य सोशल मीडिया प्लेटफॉर्म पर) को लाने के लिए एक दूसरी GraphQL(ग्राफक्यूएल) क्वेरी बनाते हैं ।

चरण 3: अलग-अलग प्रोफाइल पेज बनाएं

Next.js फ्रेमवर्क के साथ तेजी से निर्माण के बारे में सबसे अच्छी चीजों में से एक routing(रॉउटिंग) है! हमारे व्यक्तिगत प्रोफाइल पेजों मे, पेज बनाने के लिए हमें बस इतना करना है कि pages फोल्डर के तहत एक फोल्डर बनाना है जिसका नाम हमारे प्रोफाइल कॉम्पोनेन्ट के समान है। और फिर एक [id].js फ़ाइल जोड़ें जिसका उपयोग route(रूट) में पास की गयी किसी भी डायनेमिक आईडी के लिए किया जा सकता है!

जो हम कहना चाहते हैं वो यह है:

road-to-lens % mkdir pages/profile
road-to-lens % touch pages/profile/\[id\].js

हम थोड़ी देर में pages/profile/[id].js फाइल भरेंगे। लेकिन इस फ़ाइल स्ट्रक्चर के साथ, हमारा Next.js ऐप निम्नलिखित जैसे यूआरएल के लिए रिक्वेस्ट्स(requests) serve करने में सक्षम होगा:

ताकि हम प्रोफ़ाइल जानकारी प्राप्त कर सकें हमें लेंस एपीआई के लिए एक नई GraphQL रिक्वेस्ट सेट करनी होगी, और फिर हम [id].js फ़ाइल में कॉम्पोनेन्ट का निर्माण करके इसे समाप्त करेंगे।

अलग-अलग प्रोफ़ाइल जानकारी प्राप्त करने के लिए, आइए एक GraphQL डॉक्यूमेंट बनाएं जैसा कि हमने पहले संपूर्ण recommended profiles(अनुशंसित प्रोफ़ाइल) सूची लाने के लिए किया था:

road-to-lens % touch queries/fetchProfileQuery.js
// queries/fetchProfileQuery.js

import { gql } from '@apollo/client';

export default gql`
query($request: SingleProfileQueryRequest!) {
    profile(request: $request) {
        id
        name
        bio
        attributes {
          displayType
          traitType
          key
          value
        }
        followNftAddress
        metadata
        isDefault
        picture {
          ... on NftImage {
            contractAddress
            tokenId
            uri
            verified
          }
          ... on MediaSet {
            original {
              url
              mimeType
            }
          }
          __typename
        }
        handle
        coverPicture {
          ... on NftImage {
            contractAddress
            tokenId
            uri
            verified
          }
          ... on MediaSet {
            original {
              url
              mimeType
            }
          }
          __typename
        }
        ownedBy
        dispatcher {
          address
          canUseRelay
        }
        stats {
          totalFollowers
          totalFollowing
          totalPosts
          totalComments
          totalMirrors
          totalPublications
          totalCollects
        }
        followModule {
          ... on FeeFollowModuleSettings {
            type
            amount {
              asset {
                symbol
                name
                decimals
                address
              }
              value
            }
            recipient
          }
          ... on ProfileFollowModuleSettings {
            type
          }
          ... on RevertFollowModuleSettings {
            type
          }
        }
    }
  }
`;

अब इस क्वेरी का उपयोग व्यक्तिगत(individual) प्रोफाइल पेज में करते हैं!

// pages/profile/[id].js

import { useQuery } from "@apollo/client";
import { useRouter } from "next/router";
import fetchProfileQuery from "../../queries/fetchProfileQuery.js";

import Profile from "../../components/Profile.js";

export default function ProfilePage() {
  const router = useRouter();
  const { id } = router.query;

  console.log("fetching profile for", id);
  const { loading, error, data } = useQuery(fetchProfileQuery, {
    variables: { request: { profileId: id } },
  });

  if (loading) return "Loading..";
  if (error) return `Error! ${error.message}`;

  console.log("on profile page data: ", data);

  return <Profile profile={data.profile} displayFullProfile={true}/>
}

यदि आप हमारे द्वारा जोड़ी गई फ़ाइल पर एक नज़र डालते हैं, तो आप एक परिचित useQuery() कॉल देखेंगे जो fetchProfileQuery GraphQL डॉक्यूमेंट लेता है जिसे "../../queries/fetchProfileQuery.js" से आयात किया गया है।

नया इसमें यह है कि अब हम useRouter() हुक भी पेश कर रहे हैं। यह हुक हमें रनटाइम पर url में क्वेरी पैराम्स(query params) से जो भी वेरिएबल [id] वैल्यू है, उसे लेने की अनुमति देता है, और फिर हमारे कोड में इसका उपयोग करता है।

इसका अर्थ है कि यदि हमारे पास ये URL हैं:

useRouter() कॉल const { id } = router.query; के लिए निम्नलिखित आईडीज प्राप्त करेगा; :

  • 0x9752
  • 0x25c4

और फिर हम उन ids का उपयोग fetchProfileQuery में पास करने के लिए करते हैं ताकि हम उस व्यक्ति की केवल प्रोफ़ाइल प्राप्त कर सकें जिसे हम इस समय ब्राउज़ कर रहे हैं। आप Get Profile request के लिए पूर्ण डॉक्यूमेंटेशन यहां प्राप्त कर सकते हैं।

ठीक। सेव करें। पेज रिफ्रेश करें! आपको क्या दिख रहा है?

991

id: 0x9752

968

id: 0x25c4

यह Vitto और मैं हु !!

ठीक है अगर अब तक आपके लिए सब कुछ ठीक चल रहा है - यहाँ तक पहुँचने का जश्न मनाना सुनिश्चित करें (या यदि आप फंस गए हैं तो मदद मांगें)।

किसी प्रोफ़ाइल का स्क्रीनशॉट लें और उसके बाद:

अपने आधे से ज़रा पूर्ण कर लिए है!

चरण 4. प्रोफाइल पेज पर उपयोगकर्ता(user) पोस्ट लोड करें

उपयोगकर्ता द्वारा किए गए कुछ पोस्ट को लाकर प्रोफ़ाइल पेजों को थोड़ा और दिलचस्प बनाते हैं।

लेंस प्रोटोकॉल में -- पोस्ट, कमैंट्स और मिरर्स (उर्फ "री-ट्वीट") -- सभी को Publications डेटा मॉडल के अंतर्गत वर्गीकृत(classify) किया गया है।

आप पब्लिकेशन एन्डपॉइंट्स के लिए डॉक्यूमेंटेशन यहां प्राप्त कर सकते हैं। हम GET पब्लिकेशन ऑपरेशन पर ध्यान केंद्रित करेंगे।

एक बार फिर, उसी क्रम का अनुसरण करते हैं:

  1. GraphQL(ग्राफ़क्यूएल) क्वेरी डॉक्यूमेंट सेट करें।
  2. नई GraphQL क्वेरी का उपयोग करने के लिए प्रोफाइल पेज को अपडेट करें।
  3. "posts" के लिए एक नई कम्पोनेंट परिभाषा(definition) बनाएँ।
  4. लेंस एपीआई से लौटाए गए डेटा को नए कम्पोनेंट्स में पास करें।
  5. पृष्ठ का परीक्षण (test) और डिबग करें!

1. GraphQL क्वेरी डॉक्यूमेंट सेट करें

इस बार हमें बिल्कुल नई डॉक्यूमेंट फ़ाइल की आवश्यकता नहीं होगी। हम क्वेरी को मॉडिफाई कर सकते हैं जो हमने fetchProfileQuery.js में शुरू की गई और उसी रिक्वेस्ट में publications के बारे में पूछ सकते हैं। यही ग्राफक्यूएल की शक्ति है! REST API कॉल के विपरीत, आप एक अनुरोध में जितना चाहें उतना या कम अनुरोध कर सकते हैं। आइए इसे queries/fetchProfileQuery.js में अपडेट करें:

import { gql } from "@apollo/client";

export default gql`
  query (
    $request: SingleProfileQueryRequest!
    $publicationsRequest: PublicationsQueryRequest!
  ) {
    publications( request: $publicationsRequest) {
      items {
        __typename
        ... on Post {
          ...PostFields
        }
        ... on Comment {
          ...CommentFields
        }
        ... on Mirror {
          ...MirrorFields
        }
      }
      pageInfo {
        prev
        next
        totalCount
      }
    }
    profile(request: $request) {
      id
      name
      bio
      attributes {
        displayType
        traitType
        key
        value
      }
      followNftAddress
      metadata
      isDefault
      picture {
        ... on NftImage {
          contractAddress
          tokenId
          uri
          verified
        }
        ... on MediaSet {
          original {
            url
            mimeType
          }
        }
        __typename
      }
      handle
      coverPicture {
        ... on NftImage {
          contractAddress
          tokenId
          uri
          verified
        }
        ... on MediaSet {
          original {
            url
            mimeType
          }
        }
        __typename
      }
      ownedBy
      dispatcher {
        address
        canUseRelay
      }
      stats {
        totalFollowers
        totalFollowing
        totalPosts
        totalComments
        totalMirrors
        totalPublications
        totalCollects
      }
      followModule {
        ... on FeeFollowModuleSettings {
          type
          amount {
            asset {
              symbol
              name
              decimals
              address
            }
            value
          }
          recipient
        }
        ... on ProfileFollowModuleSettings {
          type
        }
        ... on RevertFollowModuleSettings {
          type
        }
      }
    }
  }

  fragment MediaFields on Media {
    url
    mimeType
  }

  fragment ProfileFields on Profile {
    id
    name
    bio
    attributes {
      displayType
      traitType
      key
      value
    }
    isFollowedByMe
    isFollowing(who: null)
    followNftAddress
    metadata
    isDefault
    handle
    picture {
      ... on NftImage {
        contractAddress
        tokenId
        uri
        verified
      }
      ... on MediaSet {
        original {
          ...MediaFields
        }
      }
    }
    coverPicture {
      ... on NftImage {
        contractAddress
        tokenId
        uri
        verified
      }
      ... on MediaSet {
        original {
          ...MediaFields
        }
      }
    }
    ownedBy
    dispatcher {
      address
    }
    stats {
      totalFollowers
      totalFollowing
      totalPosts
      totalComments
      totalMirrors
      totalPublications
      totalCollects
    }
    followModule {
      ... on FeeFollowModuleSettings {
        type
        amount {
          asset {
            name
            symbol
            decimals
            address
          }
          value
        }
        recipient
      }
      ... on ProfileFollowModuleSettings {
        type
      }
      ... on RevertFollowModuleSettings {
        type
      }
    }
  }

  fragment PublicationStatsFields on PublicationStats {
    totalAmountOfMirrors
    totalAmountOfCollects
    totalAmountOfComments
  }

  fragment MetadataOutputFields on MetadataOutput {
    name
    description
    content
    media {
      original {
        ...MediaFields
      }
    }
    attributes {
      displayType
      traitType
      value
    }
  }

  fragment Erc20Fields on Erc20 {
    name
    symbol
    decimals
    address
  }

  fragment CollectModuleFields on CollectModule {
    __typename
    ... on FreeCollectModuleSettings {
      type
      followerOnly
      contractAddress
    }
    ... on FeeCollectModuleSettings {
      type
      amount {
        asset {
          ...Erc20Fields
        }
        value
      }
      recipient
      referralFee
    }
    ... on LimitedFeeCollectModuleSettings {
      type
      collectLimit
      amount {
        asset {
          ...Erc20Fields
        }
        value
      }
      recipient
      referralFee
    }
    ... on LimitedTimedFeeCollectModuleSettings {
      type
      collectLimit
      amount {
        asset {
          ...Erc20Fields
        }
        value
      }
      recipient
      referralFee
      endTimestamp
    }
    ... on RevertCollectModuleSettings {
      type
    }
    ... on TimedFeeCollectModuleSettings {
      type
      amount {
        asset {
          ...Erc20Fields
        }
        value
      }
      recipient
      referralFee
      endTimestamp
    }
  }

  fragment PostFields on Post {
    id
    profile {
      ...ProfileFields
    }
    stats {
      ...PublicationStatsFields
    }
    metadata {
      ...MetadataOutputFields
    }
    createdAt
    collectModule {
      ...CollectModuleFields
    }
    referenceModule {
      ... on FollowOnlyReferenceModuleSettings {
        type
      }
    }
    appId
    hidden
    mirrors(by: null)
    hasCollectedByMe
  }

  fragment MirrorBaseFields on Mirror {
    id
    profile {
      ...ProfileFields
    }
    stats {
      ...PublicationStatsFields
    }
    metadata {
      ...MetadataOutputFields
    }
    createdAt
    collectModule {
      ...CollectModuleFields
    }
    referenceModule {
      ... on FollowOnlyReferenceModuleSettings {
        type
      }
    }
    appId
    hidden
    hasCollectedByMe
  }

  fragment MirrorFields on Mirror {
    ...MirrorBaseFields
    mirrorOf {
      ... on Post {
        ...PostFields
      }
      ... on Comment {
        ...CommentFields
      }
    }
  }

  fragment CommentBaseFields on Comment {
    id
    profile {
      ...ProfileFields
    }
    stats {
      ...PublicationStatsFields
    }
    metadata {
      ...MetadataOutputFields
    }
    createdAt
    collectModule {
      ...CollectModuleFields
    }
    referenceModule {
      ... on FollowOnlyReferenceModuleSettings {
        type
      }
    }
    appId
    hidden
    mirrors(by: null)
    hasCollectedByMe
  }

  fragment CommentFields on Comment {
    ...CommentBaseFields
    mainPost {
      ... on Post {
        ...PostFields
      }
      ... on Mirror {
        ...MirrorBaseFields
        mirrorOf {
          ... on Post {
            ...PostFields
          }
          ... on Comment {
            ...CommentMirrorOfFields
          }
        }
      }
    }
  }

  fragment CommentMirrorOfFields on Comment {
    ...CommentBaseFields
    mainPost {
      ... on Post {
        ...PostFields
      }
      ... on Mirror {
        ...MirrorBaseFields
      }
    }
  }
`;

ध्यान दें कि हमने एक नया क्वेरी इनपुट arg(आर्ग) जोड़ा है: $publicationsRequest: PublicationsQueryRequest!. हमें यह सुनिश्चित करना होगा कि जब हम क्वेरी को फ़ायर करते हैं तो हम एक इनपुट आर्गुमेंट के रूप में इसे पास करें।

  1. नई ग्राफक्यूएल क्वेरी का उपयोग करने के लिए प्रोफाइल पेज को अपडेट करें।

प्रोफाइल पेज पर, इस कदम के लिए हमें वास्तव में केवल एक चीज की जरूरत है, वह है कि हम जो पोस्ट चाहते हैं, उसके लिए publication request(प्रकाशन अनुरोध) जोड़ें। अनुरोध इस तरह दिखना चाहिए:

publicationsRequest: {
  profileId: id,
  publicationTypes: ["POST"], // We really only want POSTs
},

तो चलिए इसे useQuery इनपुट आर्गुमेंट विकल्पों में जोड़ते हैं:

// pages/profile/[id].js

import { useQuery, useMutation } from "@apollo/client";
import { useRouter } from "next/router";
import fetchProfileQuery from "../../queries/fetchProfileQuery.js";
import Profile from "../../components/Profile.js";

export default function ProfilePage() {
  const router = useRouter();
  const { id } = router.query;

  console.log("fetching profile for", id);
  const { loading, error, data } = useQuery(fetchProfileQuery, {
    variables: {
      request: { profileId: id },
      publicationsRequest: {
        profileId: id,
        publicationTypes: ["POST"], // We really only want POSTs
      },
    },
  });

  if (loading) return "Loading..";
  if (error) return `Error! ${error.message}`;

  console.log("on profile page data: ", data);
  return (
    <div className="flex flex-col p-8 items-center">
      <Profile profile={data.profile} displayFullProfile={true} />
    </div>
  );
}
  1. "posts" के लिए एक नई कॉम्पोनेन्ट डेफिनिशन बनाएँ।

उपयोगकर्ता के पोस्ट के संबंध में प्राप्त डेटा को प्रदर्शित करने के लिए हमें एक नए रिएक्ट कॉम्पोनेन्ट की आवश्यकता है। आइए components/Post.js में एक बनाएं:

// components/Post.js
export default function Post(props) {
  const post = props.post;

  return (
    <div className="p-8">
      <div className="max-w-md mx-auto bg-white rounded-xl shadow-md overflow-hidden md:max-w-2xl">
        <div className="md:flex">
          <div className="p-8">
            <p className="mt-2 text-xs text-slate-500 whitespace-pre-line">
              {post.metadata.content}
            </p>
          </div>
        </div>
      </div>
    </div>
  );
}

यदि आप इस सरल कॉम्पोनेन्ट की तुलना Profile कॉम्पोनेन्ट से करते हैं, तो आप देखेंगे कि वे दोनों एक बहुत ही सरल पैटर्न का पालन करते हैं, जो यह है कि वे एक props ऑब्जेक्ट को स्वीकार करते हैं जिसमें कॉम्पोनेन्ट द्वारा इसके कुछ हिस्सों को render/प्रस्तुत करने के लिए आवश्यक डेटा होता है।

इस मामले में, हमें वास्तव में posts से केवल एक मुख्य जानकारी की आवश्यकता है:

  • post.metadata.content

यह देखते हुए कि यह metadata फ़ील्ड की ही हमें आवश्यकता थी, आप वास्तव में बाद में GraphQL डॉक्यूमेंट पर वापस जा सकते हैं और उन फ़ील्ड्स को हटा सकते हैं जिनका हम अंतिम फीचर( end feature) में उपयोग नहीं कर रहे हैं।

यह कॉम्पोनेन्ट स्टाइल करने के लिए Tailwind CSS का भी उपयोग करता है।

  1. लेंस एपीआई से लौटाए गए डेटा को नए घटकों में पास करें

प्रोफाइल पेज पर वापस जाएं (pages/profile/[id].js):

अब हम <Post/> कॉम्पोनेंट को प्रोफाइल पेज पर इम्पोर्ट कर सकते हैं और इसका उपयोग उन सभी पोस्ट को रेंडर करने के लिए कर सकते हैं जो हमें एपीआई से वापस मिलती हैं।

import { useQuery, useMutation } from "@apollo/client";
import { useRouter } from "next/router";
import fetchProfileQuery from "../../queries/fetchProfileQuery.js";
import Profile from "../../components/Profile.js";
import Post from "../../components/Post.js";

export default function ProfilePage() {
  const router = useRouter();
  const { id } = router.query;

  console.log("fetching profile for", id);
  const { loading, error, data } = useQuery(fetchProfileQuery, {
    variables: {
      request: { profileId: id },
      publicationsRequest: {
        profileId: id,
        publicationTypes: ["POST"],
      },
    },
  });

  if (loading) return "Loading..";
  if (error) return `Error! ${error.message}`;

  return (
    <div className="flex flex-col p-8 items-center">
      <Profile profile={data.profile} displayFullProfile={true} />
      {data.publications.items.map((post, idx) => {
        return <Post key={idx} post={post}/>;
      })}
    </div>
  );
}

स्निपेट(snippet) के नीचे ध्यान दें कि यह वास्तव में कोड की ये पंक्तियां थीं:

{data.publications.items.map((post, idx) => {
        return <Post key={idx} post={post}}/>;
 })}

जो इस चरण में मुश्किल काम कर रहे हैं। लेकिन इसके साथ, हमें अपनी posts फीचर को आज़माने में सक्षम होना चाहिए!

  1. परीक्षण और डिबग

यदि सब कुछ ठीक रहा, तो अब आप प्रोफ़ाइल कार्ड के निचे पॉप्युलेट की गई पोस्ट देख सकेंगे! 🎉

856

बधाई हो, आप डिसेंट्रलाइज़्ड सोशल मीडिया डेवलपर बनने की राह पर हैं :)

अन्य एपीआई और संसाधन(resources)

उम्मीद है, इस समय तक, आपने कई अन्य रोड टू वेब3(Road to Web3) चुनौतियों को पूरा कर लिया है और आपने इस बिंदु तक पहुंचने की प्रक्रिया का आनंद लिया है। इस सप्ताह का पाठ(lesson) एनएफटी और विकेंद्रीकृत(डिसेंट्रलाइज़्ड) प्रोटोकॉल के ऊपर(शीर्ष पर) निर्मित एपीआई का उपयोग करने के बारे में था।

हम आपको सुझाव देंगे कि आप लेंस डॉक्यूमेंटेशन को पढ़ने के लिए समय निकालें, विभिन्न एन्डपॉइंट्स(endpoints) को आज़माएँ, और इस रेपो में होस्ट किए गए सैंपल(sample) कोड को पढ़ें:

यहाँ कुछ अन्य वेब3 APIs हैं जो अत्याधुनिक हैं:

  • The MintKudos PoK Tokens APIs(मिंटकुडोस पीओके टोकन एपीआई) - Kei
    @ कंट्रीब्यूशन लैब्स द्वारा बनाया गया
  • लिट प्रोटोकॉल एसडीके और ट्यूटोरियल - Deb @ लिट प्रोटोकॉल द्वारा बनाया गया

आपके हाथों में इन टूल और डेटा एक्सेस के साथ, हम वास्तव में यह देखने के लिए उत्साहित हैं की आप इनका उपयोग कर के क्या निर्मित करते हैं 🙂

एक Web3 योग सोशल नेटवर्क?

केवल रोड टू वेब3 PoK टोकन धारकों(holders) के लिए एक विशेष हैंगआउट स्थान?

एक नए जमाने का ऑनलाइन क्लासरूम जहां शिक्षकों को आपके डीऐप के माध्यम से कंटेंट और शिक्षा के लिए सीधे छात्रों द्वारा भुगतान किया जाता है?

संभावनाएं अनंत हैं 🔥🔥

चुनौतियों

इस सप्ताह की सामग्री जटिल(complex) है, इसलिए हम आपको जो चुनौती विकल्प दे रहे हैं वह जानबूझकर अस्पष्ट(vague) हैं ताकि आप जैसे चाहें एक्स्प्लोर कर सकें। नीचे दिए गए विकल्पों में से कम से कम एक चुनें और देखें कि आप इसके साथ क्या प्राप्त कर सकते हैं!

  1. लेंस एपीआई का उपयोग करके, एक नई जानकारी की क्वेरी करें जो ट्यूटोरियल में शामिल नहीं थी और इसे अपनी साइट पर प्रदर्शित करें।
  2. लेंस एपीआई का उपयोग करके, reacting(प्रतिक्रिया करने), following(फॉलो करने, या नए publications(पब्लिकेशन्स) बनाने जैसे एक्शन्स के लिए authentication(ऑथेंटिकेशन) और mutations(उत्परिवर्तन/मिउटेशन्स) कैसे करें, इसका पता लगाएं।
  3. Repl.it या Vercel जैसी सेवा का उपयोग करके अपने एप्लिकेशन को वर्ल्ड वाइड वेब पर डिप्लॉय करें।
  4. अपने लेंस प्रोटोकॉल पोस्ट तक टोकन-गेट एक्सेस के लिए Lit Protocol SDK(लिट प्रोटोकॉल एसडीके) का उपयोग करें ताकि केवल मिंटकुडोस टोकन धारक ही उन्हें देख सकें।

अपना कार्य यहां सबमिट करना न भूलें ताकि आप इस सप्ताह के Proof of Knowledge(ज्ञान का प्रमाण) NFT प्राप्त कर सकें!

Alchemy University Discord(अल्केमी विश्वविद्यालय डिस्कॉर्ड): [https://university.alchemy.com/discord)

बधाई हो!

आपने लेंस प्रोटोकॉल का उपयोग करके सफलतापूर्वक नेविगेट करना, क्वेरी करना और निर्माण करना सीख लिया है! 🔥

इस ट्यूटोरियल का वीडियो संस्करण चाहते हैं? अल्केमी YouTube चैनल को सब्सक्राइब करें और हमारे डिस्कोर्ड कम्युनिटी में शामिल हों जहां हजारों डेवलपर्स आपकी मदद करने के लिए तैयार हैं।


ReadMe