[ad_1]
Dear Elon,
First of all, Twitter is a special application for me. Since you have always mentioned the importance of decentralization after purchasing Twitter, I am watching the progress with excitement. Although brought up with layoffs, you and your team seem to have the vision to leverage this great platform to launch a more mature and self-sufficient web.
With the right design, a decentralized Twitter can drive a new kind of internet, fundamentally changing the relationship between users and platforms. People can own data they create in apps on the web. It has been released on many platforms.
A look at Twitter’s (TWTR) product lines, such as profiles, posts, messaging, and advertising, can provide insight into what the self-driving web will look like and how to recruit people.
As you can see from your past correspondence with former Twitter CEO Jack Dorsey, you believe that the usefulness of Twitter today is at its limits. Much can be done, such as becoming more useful to users and evolving as a protocol rather than a company.
This model can be extended to social media using systems such as Filecoin, Arweave, Ceramic and Polybase. Users’ links and posts are not limited to a single wall like Twitter or Facebook, they live on the open web. This data can still be private and allowed (and thus translated into a revenue model) through threshold cryptography-assisted access control.
In decentralized, user-dominated and privacy-focused social media protocols, users will have the ability to allow which apps and links can see their content on interface platforms just like email.
One of the biggest hurdles in Web3 is the wallet user experience. For some people self-storage of keys is a privilege, but for more it’s a burden.
MPC wallets enable users to sign in to applications with a traditional login, pin code and biometric information with their wallet, without the burden of personal surveillance or having to rely on a central keyholder registrar.
There are already a number of active projects building open source infrastructure for the public parts of social charts like Twitter today to help people own their own data. Lens Protocol and Orbis are live now.
Not all social media data is public. Key components in social networking and media, such as a person’s links, profile information, posts, and messages, are often private, meaning they can only be viewed by authorized people. Or at least, it is preferable to have this option.
Today, on Twitter, this data is largely locked on Twitter and Facebook servers. The alternative is to store this data encrypted on the open web and give the user granular access control over who can decrypt it. Elon claimed that next week the service will open-source its algorithm. Meanwhile, with a more surprising decision, he announced that the SMS service, which is used for two-step security, can only be used by Blue members.
This is not a new idea in online social networks, and this system is synchronized with the Pretty Good Privacy(PGP) encryption program in the early days of the web and still used by many. PGP allows people to send their public keys anywhere to receive messages that only the owner of the private key can read.
Threshold cryptography enhances these privacy standards by allowing users to create rules about who can see their posts. If decentralized social protocols are widely adopted, this encrypted data can be used on the open web.
Advertising is often considered a problematic issue in certain Web3 circles, but the reality is that only about 10% of internet users pay for premium apps and software. This means that most people use software services with advertisements.
Advertisers for the purchase on the publisher’s page, the publisher is paid from the ad.
In this case, it can be decentralized, the aforementioned system for user data can also be applied for ad attribution. The last click is written to a person’s data center (encrypted and stored on the open web) as a verifiable credential, after which the advertiser can obtain permission to decrypt this data.
As authorities continue to push for tracking cookies, bringing the user and their consent to track ad clicks and conversions offers a long-term path for ad-supported publishers.
There has been a lot of discussion lately about how much control social media giants like Twitter have. What can we do about it together with Elon? Let’s take a look at how a decentralized Twitter architecture and development should be. According to the latest posts, the current architecture is as follows;
Friends in the Aave team, which we can use for the Twitter infrastructure, started a project called Lens Protocol. It is one of the most exciting technologies to enter the web3 ecosystem lately because it uses blockchain technology to give back the power and control of data to the users who created it.
What kind of architecture do we think about in decentralized Twitter?
We will create Next.js application with Apollo GraphQL client.
We are considering using the Lens protocol API to retrieve profiles, posts and other data stored on the Polygon blockchain.
MintKudos API we will be able to integrate your PoK tokens into our dApp.
Lit Protocol we will be able to encrypt certain posts so that they are only visible to various community members.
How will we deploy your decentralized social media application front-end website using Vercel?
Let’s rebuild Twitter for Elon with Lens Protocol.
Lens Protocol also has a list of community-created web apps, including Lensfrens, Lenster, Phaver, Alps Finance, Refract, and more.
Let’s take a look at other applications;
Phaver; The lens-powered social mobile app also lets you share other people’s posts to improve, allowing people to earn money for their content.
Refract; It’s like Hacker News, but all links and posts shared here are powered by the Lens Protocol.
All of these apps are built by different people, different teams, with different user experiences and product goals, but all the core data is the same, as if they were all sharing the same database and APIs.
How can it be?
It turns out that a shared database and public API are exactly the core ideas of the Lens Protocol. This is why this technology has so much potential. Each piece of data is an NFT.
Every post, every comment, every reaction, every follow. Each of these pieces of data is stored as an in-kind token created and controlled by you as its creator.
This means that the digital content and relationships we create as users belong to us and can be imported into any application built on top of the protocol!
Let’s look at the application Let’s install Apollo for the Next.js application. We will use the Apollo Client to query the Lens Protocol with the data.
npm install @apollo/client graphql
npm run dev
Let’s use Apollo and GraphQL by integrating lens profiles into the Twitter profile page.
touch apollo-client.js
Here we will start a client that the base url points to. This is how we will use the Lens MATIC(Polygon) mainnet API.
// ./apollo-client.jsimport { ApolloClient, InMemoryCache } from "@apollo/client";
const client = new ApolloClient({
uri: "https://api.lens.dev",
cache: new InMemoryCache(),
});
export default client;
Once this GraphQL client is started we can import it into our file and use it as a global application component.
// pages/_app.jsimport '../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
Here you can see we added it as a wrapper. We can use helper methods to get data and send updates from Lens API.
One final update before checking localhost again. To make a query to get Recommended profiles from Lens;
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>
)
}
All this data here is stored as NFTs on the Polygon blockchain. What the Lens team does with their API is index data on the entire chain to make it easy for developers to fetch and create using NFTs!
// pages/_app.jsimport '../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
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>
)
}
// queries/recommendedProfilesQuery.jsimport {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
}
}
}
}
`;
mkdir components
touch components/user/user-profile.tsx
// components/user/user-profile.tsximport 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>
);
}
We can also add the profile picture and the number of followers and followed.
One of the great things about building quickly with the Next.js framework is routing. For our individual profile pages, all we have to do to create the page is create a folder under the folder with the same name as our component. Then we add a file that can be used for any dynamic id passed to the route.
mkdir pages/user
touch pages/user/\[id\].js
So let’s finish this by setting up a new GraphQL request to the Lens API to fetch the profile information, and then we’ll finish by creating the component in the file.
To get individual profile information, let’s create a GraphQL document like we did earlier to get the full list of suggested profiles:
touch queries/fetchProfileQuery.js
// queries/fetchProfileQuery.jsimport { 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
}
}
}
}
`;
Now let’s use this query on individual profile page.
// pages/user/[id]/index.tsximport { 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}/>
}
Let’s create a new component definition for posts.
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
}
}
}
`;
On the profile page, all we really need to do for this step is add a post request for the posts we want.
publicationsRequest: {
profileId: id,
publicationTypes: ["POST"], // We really only want POSTs
},
// pages/profile/[id].jsimport { 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>
);
}
A new component is needed to display received data on user posts.
// 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>
);
}
Now we can import the component into the profile page and use it to process any posts we get back from the API.
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>
);
}
Let’s actually look at these lines of code;
{data.publications.items.map((post, idx) => {
return <Post key={idx} post={post}}/>;
})}
See you in next article…
New to trading? Try crypto trading bots or copy trading on best crypto exchanges
Join Coinmonks Telegram Channel and Youtube Channel get daily Crypto News
[ad_2]
Source link