diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..e1b2078 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,25 @@ +# Contributing to React Native Lens UI Kit 🌿 + +To run and develop with the project locally, do the following: + +1. Clone the repo: + +```sh +git clone git@github.com:lens-protocol/react-native-lens-ui-kit.git +``` + +2. Install the dependencies + +```sh +npm install + +# or use yarn, pnpm, etc.. +``` + +3. Open `watcher.js` and configure the directory of your React Native project (`destDir`). + +4. Run the develop scripts: + +```sh +npm run dev +``` diff --git a/License b/License new file mode 100644 index 0000000..14035a2 --- /dev/null +++ b/License @@ -0,0 +1,7 @@ +# ISC License (ISC) + +Copyright 2023 Lens Protocol + +Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/README.md b/README.md index 06ef25f..c2ac47e 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,22 @@ A React Native UI kit for [Lens Protocol](https://lens.xyz/). Get started buildi Example app codebase located [here](https://github.com/dabit3/dabit3-react-native-lens-example) +## Table of Contents + +- [Getting Started](#getting-started) + - [Prerequisites](#prerequisites) + - [Installation](#prerequisites) +- [Components](#components) + - [Feed](#feed) + - [Profiles](#profiles) + - [Profile Header](#profile-header) + - [Publication](#ublication) + - [ProfileListItem](#profilelistitem) + - [LensProvider](#lensprovider) +- [Roadmap](#roadmap) + - [Beta Roadmap](#beta-roadmap) + - [V1 Roadmap](#v1-roadmap) + ## Getting started 🚀 ### Prerequisites @@ -25,9 +41,9 @@ npm install @lens-protocol/react-native-lens-ui-kit A feed of posts from Lens. ```tsx -import { Feed } from '@lens-protocol/react-native-lens-ui-kit' +import { Feed } from "@lens-protocol/react-native-lens-ui-kit"; - +; ``` ### Default props @@ -78,13 +94,13 @@ styles = StyleSheet.create({ ### Query options for `Feed` -__explorePublications (default)__ +**explorePublications (default)** [explorePublications](./src/graphql/explorePublications.graphql) -__getPublications__ +**getPublications** [getPublications](./src/graphql/getPublications.graphql) -__getComments__ +**getComments** [getPublications](./src/graphql/getPublications.graphql) ## Profiles @@ -92,9 +108,9 @@ __getComments__ A list of profiles ```tsx -import { Profiles } from '@lens-protocol/react-native-lens-ui-kit' +import { Profiles } from "@lens-protocol/react-native-lens-ui-kit"; - +; ``` ### Default Props @@ -117,23 +133,20 @@ onProfilePress = profile => console.log({ profile }) ### Query options for `Profiles` -__exploreProfiles (default)__ +**exploreProfiles (default)** [exploreProfiles](./src/graphql/exploreProfiles.graphql) -__getFollowing__ +**getFollowing** [getFollowing](./src/graphql/getFollowing.graphql) - ## Profile Renders an individual profile ```tsx -import { Profile } from '@lens-protocol/react-native-lens-ui-kit' +import { Profile } from "@lens-protocol/react-native-lens-ui-kit"; - +; ``` ### Default props @@ -163,8 +176,9 @@ onLikePress = publication => console.log({ publication }) ``` ### Styles -publicationStyles = [PublicationStyles](https://github.com/lens-protocol/react-native-lens-ui-kit/blob/main/src/types.ts#L137) -headerStyles = [ProfileHeaderStyles](https://github.com/lens-protocol/react-native-lens-ui-kit/blob/main/src/types.ts#L157) + +publicationStyles = [PublicationStyles](https://github.com/lens-protocol/react-native-lens-ui-kit/blob/main/src/types.ts#L137) +headerStyles = [ProfileHeaderStyles](https://github.com/lens-protocol/react-native-lens-ui-kit/blob/main/src/types.ts#L157) feedStyles = [FeedStyles](https://github.com/lens-protocol/react-native-lens-ui-kit/blob/main/src/types.ts#L188) ## Profile Header @@ -172,12 +186,12 @@ feedStyles = [FeedStyles](https://github.com/lens-protocol/react-native-lens-ui- Renders a profile header component. ```tsx -import { ProfileHeader } from '@lens-protocol/react-native-lens-ui-kit' +import { ProfileHeader } from "@lens-protocol/react-native-lens-ui-kit"; +/>; ``` ### Default props @@ -189,7 +203,8 @@ onFollowingPress = profile => console.log({ profile }) onFollowersPress = profile => console.log({ profile }) ``` -### Styles +### Styles + [ProfileHeaderStyles](https://github.com/lens-protocol/react-native-lens-ui-kit/blob/main/src/types.ts#L157) ## Publication @@ -197,11 +212,9 @@ onFollowersPress = profile => console.log({ profile }) Renders an individual publication. ```tsx -import { Publication } from '@lens-protocol/react-native-lens-ui-kit' +import { Publication } from "@lens-protocol/react-native-lens-ui-kit"; - +; ``` ### Default props @@ -222,7 +235,8 @@ onLikePress = publication => console.log({ publication }) onProfileImagePress = publication => console.log({ publication }) ``` -### Styles +### Styles + [PublicationStyles](https://github.com/lens-protocol/react-native-lens-ui-kit/blob/main/src/types.ts#L137) ## ProfileListItem @@ -230,11 +244,9 @@ onProfileImagePress = publication => console.log({ publication }) Renders a list item for a profile overview. ```tsx -import { ProfileListItem } from '@lens-protocol/react-native-lens-ui-kit' +import { ProfileListItem } from "@lens-protocol/react-native-lens-ui-kit"; - +; ``` ### Default props @@ -246,7 +258,8 @@ onProfilePress onFollowPress ``` -### Styles +### Styles + [ProfileListItemStyles](https://github.com/lens-protocol/react-native-lens-ui-kit/blob/main/src/types.ts#L173) ## LensProvider @@ -266,15 +279,12 @@ theme? = 'light' (default) | 'dark import { LensProvider, Theme, - Environment -} from '@lens-protocol/react-native-lens-ui-kit' + Environment, +} from "@lens-protocol/react-native-lens-ui-kit"; - + - +; ``` # Roadmap @@ -297,29 +307,3 @@ Currently this project is in Alpha. - Improved theme-ing - Better TypeScript support - Support audio - -### Contribute - -To run and develop with the project locally, do the following: - -1. Clone the repo: - -```sh -git clone git@github.com:lens-protocol/react-native-lens-ui-kit.git -``` - -2. Install the dependencies - -```sh -npm install - -# or use yarn, pnpm, etc.. -``` - -3. Open `watcher.js` and configure the directory of your React Native project (`destDir`). - -4. Run the develop scripts: - -```sh -npm run dev -``` \ No newline at end of file diff --git a/src/api.ts b/src/api.ts index e936c6e..18b3c8e 100644 --- a/src/api.ts +++ b/src/api.ts @@ -1,16 +1,16 @@ -import { createClient as createUrqlClient } from 'urql' -import { Environment } from './types' +import { createClient as createUrqlClient } from "urql"; +import { Environment } from "./types/types"; const environments = { - testnet: 'https://api-mumbai.lens.dev', - sandbox: 'https://api-sandbox-mumbai.lens.dev', - mainnet: 'https://api.lens.dev' -} + testnet: "https://api-mumbai.lens.dev", + sandbox: "https://api-sandbox-mumbai.lens.dev", + mainnet: "https://api.lens.dev", +}; /* creates the API client */ -export function createClient(env:Environment = Environment.mainnet) { - const APIURL = environments[env] || environments.mainnet +export function createClient(env: Environment = Environment.mainnet) { + const APIURL = environments[env] || environments.mainnet; return createUrqlClient({ - url: APIURL - }) -} \ No newline at end of file + url: APIURL, + }); +} diff --git a/src/codegen.yaml b/src/codegen.yaml index ca3abb2..463f877 100644 --- a/src/codegen.yaml +++ b/src/codegen.yaml @@ -1,7 +1,5 @@ - - -schema: 'https://api.lens.dev' -documents: './src/graphql/*.graphql' +schema: "https://api.lens.dev" +documents: "./src/graphql/**/*.graphql" generates: ./src/graphql/generated.ts: plugins: @@ -11,4 +9,4 @@ generates: - fragment-matcher config: fetcher: fetch - dedupeFragments: true \ No newline at end of file + dedupeFragments: true diff --git a/src/components/Feed.tsx b/src/components/Feed.tsx index 0a980bf..106f8df 100644 --- a/src/components/Feed.tsx +++ b/src/components/Feed.tsx @@ -1,12 +1,12 @@ -import { useState, useEffect, useContext } from 'react' +import { useState, useEffect, useContext } from "react"; import { View, FlatList, Text, StyleSheet, - ActivityIndicator -} from 'react-native' -import { createClient } from '../api' + ActivityIndicator, +} from "react-native"; +import { createClient } from "../api"; import { ProfileMetadata, FeedQuery, @@ -14,25 +14,29 @@ import { PublicationStyles, FeedStyles, PublicationFetchResults, - LensContextType -} from '../types' -import { configureIPFSURL } from '../utils' -import { Publication as PublicationComponent } from './' + LensContextType, +} from "../types/types"; +import { configureIPFSURL } from "../utils/utils"; +import { Publication as PublicationComponent } from "./"; import { ExplorePublicationsDocument, PublicationsDocument, PaginatedResultInfo, PublicationTypes, - PublicationSortCriteria -} from '../graphql/generated' -import { LensContext } from '../context' + PublicationSortCriteria, +} from "../graphql/generated"; +import { LensContext } from "../context/context"; export function Feed({ query = { name: "explorePublications", - publicationTypes: [PublicationTypes.Post, PublicationTypes.Comment, PublicationTypes.Mirror], + publicationTypes: [ + PublicationTypes.Post, + PublicationTypes.Comment, + PublicationTypes.Mirror, + ], sortCriteria: PublicationSortCriteria.Latest, - limit: 20 + limit: 20, }, ListHeaderComponent, ListFooterComponent, @@ -44,205 +48,220 @@ export function Feed({ hideCollects = false, iconColor, infiniteScroll = true, - onEndReachedThreshold = .65, - onCollectPress = publication => console.log({ publication }), - onCommentPress = publication => console.log({ publication }), - onMirrorPress = publication => console.log({ publication }), - onLikePress = publication => console.log({ publication }), - onProfileImagePress = publication => console.log({ publication }), + onEndReachedThreshold = 0.65, + onCollectPress = (publication) => console.log({ publication }), + onCommentPress = (publication) => console.log({ publication }), + onMirrorPress = (publication) => console.log({ publication }), + onLikePress = (publication) => console.log({ publication }), + onProfileImagePress = (publication) => console.log({ publication }), publicationStyles, styles = baseStyles, }: { - query?: FeedQuery, - ListHeaderComponent?: React.FC, - ListFooterComponent?: React.FC <{}>, - signedInUser?: ProfileMetadata - feed?: ExtendedPublication[], - onCollectPress?: (publication: ExtendedPublication) => void, - onCommentPress?: (publication: ExtendedPublication) => void, - onMirrorPress?: (publication: ExtendedPublication) => void, - onLikePress?: (publication: ExtendedPublication) => void, - onProfileImagePress?: (publication: ExtendedPublication) => void, - hideLikes?: any, - hideComments?: boolean, - hideMirrors?: boolean, - hideCollects?: boolean, - iconColor?: string, - infiniteScroll?: boolean, - onEndReachedThreshold?: number, - styles?: FeedStyles, - publicationStyles?: PublicationStyles + query?: FeedQuery; + ListHeaderComponent?: React.FC; + ListFooterComponent?: React.FC<{}>; + signedInUser?: ProfileMetadata; + feed?: ExtendedPublication[]; + onCollectPress?: (publication: ExtendedPublication) => void; + onCommentPress?: (publication: ExtendedPublication) => void; + onMirrorPress?: (publication: ExtendedPublication) => void; + onLikePress?: (publication: ExtendedPublication) => void; + onProfileImagePress?: (publication: ExtendedPublication) => void; + hideLikes?: any; + hideComments?: boolean; + hideMirrors?: boolean; + hideCollects?: boolean; + iconColor?: string; + infiniteScroll?: boolean; + onEndReachedThreshold?: number; + styles?: FeedStyles; + publicationStyles?: PublicationStyles; }) { - const [publications, setPublications] = useState([]) - const [paginationInfo, setPaginationInfo] = useState() - const [loading, setLoading] = useState(false) - const [canPaginate, setCanPaginate] = useState(true) + const [publications, setPublications] = useState([]); + const [paginationInfo, setPaginationInfo] = useState< + PaginatedResultInfo | undefined + >(); + const [loading, setLoading] = useState(false); + const [canPaginate, setCanPaginate] = useState(true); + + const { environment } = useContext(LensContext) as LensContextType; + const client = createClient(environment); - const { environment } = useContext(LensContext) as LensContextType - const client = createClient(environment) - useEffect(() => { - fetchPublications() - }, []) + fetchPublications(); + }, []); async function fetchResponse(cursor?: string) { - if (query.name === 'explorePublications') { + if (query.name === "explorePublications") { try { - let { data } = await client.query(ExplorePublicationsDocument, { - request: { - cursor, - publicationTypes: query.publicationTypes, - sortCriteria: query.sortCriteria || PublicationSortCriteria.Latest, - limit: query.limit - } - }).toPromise() + let { data } = await client + .query(ExplorePublicationsDocument, { + request: { + cursor, + publicationTypes: query.publicationTypes, + sortCriteria: + query.sortCriteria || PublicationSortCriteria.Latest, + limit: query.limit, + }, + }) + .toPromise(); if (data) { - const { explorePublications } = data - let { - pageInfo, - items - } = explorePublications as PublicationFetchResults + const { explorePublications } = data; + let { pageInfo, items } = + explorePublications as PublicationFetchResults; return { - pageInfo, items, - } + pageInfo, + items, + }; } } catch (err) { - console.log('Error fetching explorePublications: ', err) + console.log("Error fetching explorePublications: ", err); } } - if (query.name === 'getPublications') { - let { data } = await client.query(PublicationsDocument, { - request: { - profileId: query.profileId, - cursor, - publicationTypes: query.publicationTypes - } - }).toPromise() + if (query.name === "getPublications") { + let { data } = await client + .query(PublicationsDocument, { + request: { + profileId: query.profileId, + cursor, + publicationTypes: query.publicationTypes, + }, + }) + .toPromise(); if (data) { - const { publications: { pageInfo, items }} = data + const { + publications: { pageInfo, items }, + } = data; return { - pageInfo, items - } as PublicationFetchResults + pageInfo, + items, + } as PublicationFetchResults; } } - if (query.name === 'getComments') { + if (query.name === "getComments") { try { - let { data } = await client.query(PublicationsDocument, { - request: { - commentsOf: query.publicationId, - cursor - } - }).toPromise() + let { data } = await client + .query(PublicationsDocument, { + request: { + commentsOf: query.publicationId, + cursor, + }, + }) + .toPromise(); if (data) { - const { publications: { pageInfo, items }} = data + const { + publications: { pageInfo, items }, + } = data; return { - pageInfo, items - } as PublicationFetchResults + pageInfo, + items, + } as PublicationFetchResults; } } catch (err) { - console.log('error fetching comments...', err) + console.log("error fetching comments...", err); } } } async function fetchNextItems() { try { - if (canPaginate && paginationInfo) { - const { next } = paginationInfo - if (!next) { - setCanPaginate(false) - } else { - fetchPublications(next) - } - } + if (canPaginate && paginationInfo) { + const { next } = paginationInfo; + if (!next) { + setCanPaginate(false); + } else { + fetchPublications(next); + } + } } catch (err) { - console.log('Error fetching next items:', err) + console.log("Error fetching next items:", err); } } async function fetchPublications(cursor?: string) { try { - if ( - !feed || - feed && cursor - ) { - setLoading(true) - let { - items, - pageInfo - } = await fetchResponse(cursor) as { - pageInfo: PaginatedResultInfo, - items: ExtendedPublication[] - } - setPaginationInfo(pageInfo) - items = items.filter(item => { - const { metadata: { media } } = item + if (!feed || (feed && cursor)) { + setLoading(true); + let { items, pageInfo } = (await fetchResponse(cursor)) as { + pageInfo: PaginatedResultInfo; + items: ExtendedPublication[]; + }; + setPaginationInfo(pageInfo); + items = items.filter((item) => { + const { + metadata: { media }, + } = item; if (media.length) { if (media[0].original) { - if (media[0].original.mimeType === 'image/jpeg') return true - if (media[0].original.mimeType === 'image/gif') return true - if (media[0].original.mimeType === 'image/png') return true - return false + if (media[0].original.mimeType === "image/jpeg") return true; + if (media[0].original.mimeType === "image/gif") return true; + if (media[0].original.mimeType === "image/png") return true; + return false; } } else { - return true + return true; } - }) - items = items.map(item => { - if (item.profileSet) return item - let { profile } = item - if (item.__typename === 'Mirror') { + }); + items = items.map((item) => { + if (item.profileSet) return item; + let { profile } = item; + if (item.__typename === "Mirror") { if (item.mirrorOf) { - item.originalProfile = profile - item.stats = item.mirrorOf.stats - profile = item.mirrorOf.profile + item.originalProfile = profile; + item.stats = item.mirrorOf.stats; + profile = item.mirrorOf.profile; } } - if (profile.picture && profile.picture.__typename === 'MediaSet' && profile.picture.original) { - const url = configureIPFSURL(profile.picture.original.url) + if ( + profile.picture && + profile.picture.__typename === "MediaSet" && + profile.picture.original + ) { + const url = configureIPFSURL(profile.picture.original.url); if (url) { - profile.picture.original.url = url + profile.picture.original.url = url; } else { - profile.missingAvatar = true + profile.missingAvatar = true; } } else { - profile.missingAvatar = true + profile.missingAvatar = true; } - item.profile = profile - item.profileSet = true - return item - }) + item.profile = profile; + item.profileSet = true; + return item; + }); if (cursor) { - let newData = [...publications, ...items] + let newData = [...publications, ...items]; if (query.sortCriteria === "LATEST") { - newData = [...new Map(newData.map(m => [m.id, m])).values()] + newData = [...new Map(newData.map((m) => [m.id, m])).values()]; } - setPublications(newData) + setPublications(newData); } else { - setPublications(items) + setPublications(items); } - setLoading(false) + setLoading(false); } else { - setPublications(feed) + setPublications(feed); } } catch (err) { - console.log('error fetching publications...', err) + console.log("error fetching publications...", err); } } function onEndReached() { if (infiniteScroll) { - fetchNextItems() + fetchNextItems(); } } function renderItem({ - item, index - } : { - item: ExtendedPublication, - index: number + item, + index, + }: { + item: ExtendedPublication; + index: number; }) { return ( - ) + ); } return ( - { - !loading && + {!loading && publications.length === Number(0) && - query.name === 'getComments' && ( + query.name === "getComments" && ( No comments... - ) - } + )} + ListFooterComponent ? ( + ListFooterComponent + ) : loading ? ( + ) : null } /> - ) + ); } let baseStyles = StyleSheet.create({ container: { - flex: 1 + flex: 1, }, - loadingIndicatorStyle : { - marginVertical: 20 + loadingIndicatorStyle: { + marginVertical: 20, }, noCommentsMessage: { margin: 20, fontSize: 14, - fontWeight: '500' - } -}) + fontWeight: "500", + }, +}); diff --git a/src/components/LensProvider.tsx b/src/components/LensProvider.tsx index 1f4160b..75d9210 100644 --- a/src/components/LensProvider.tsx +++ b/src/components/LensProvider.tsx @@ -1,19 +1,23 @@ -import { LensContext } from '../context' -import { Theme, Environment } from '../types' +import { LensContext } from "../context/context"; +import { Theme, Environment } from "../types/types"; export function LensProvider({ - children, environment, theme + children, + environment, + theme, }: { - children: React.ReactNode, - theme?: Theme, - environment?: Environment + children: React.ReactNode; + theme?: Theme; + environment?: Environment; }) { return ( + environment, + theme, + }} + > {children} - ) -} \ No newline at end of file + ); +} diff --git a/src/components/Profile.tsx b/src/components/Profile.tsx deleted file mode 100644 index a69a38d..0000000 --- a/src/components/Profile.tsx +++ /dev/null @@ -1,97 +0,0 @@ -import { Feed, ProfileHeader } from './' -import { PublicationTypes, Publication } from '../graphql/generated' -import { - ProfileHeaderStyles, - FeedStyles, - PublicationStyles, - FeedQuery, - ExtendedProfile, - ExtendedPublication -} from '../types' - -export function Profile({ - profile, - ListHeaderComponent = null, - ListFooterComponent, - feed, - signedInUser, - hideLikes = false, - hideComments = false, - hideMirrors = false, - hideCollects = false, - iconColor, - infiniteScroll = true, - onEndReachedThreshold = .65, - headerStyles, - feedStyles, - publicationStyles, - query = { - name: "getPublications", - profileId: profile.id, - publicationTypes: [PublicationTypes.Post, PublicationTypes.Mirror] - }, - onFollowingPress = profile => console.log({ profile }), - onFollowersPress = profile => console.log({ profile }), - onProfileImagePress = publication => console.log({ publication }), - onCollectPress = publication => console.log({ publication }), - onCommentPress = publication => console.log({ publication }), - onMirrorPress = publication => console.log({ publication }), - onLikePress = publication => console.log({ publication }), -} : { - profile: ExtendedProfile, - ListHeaderComponent?: any, - ListFooterComponent?: React.FC <{}>, - feed?: Publication[], - signedInUser?: any, - hideLikes?: boolean, - hideComments?: boolean, - hideMirrors?: boolean, - hideCollects?: boolean, - iconColor?: string, - infiniteScroll?: boolean, - onEndReachedThreshold?: number, - headerStyles?: ProfileHeaderStyles, - feedStyles?: FeedStyles, - publicationStyles?: PublicationStyles, - query?: FeedQuery, - onFollowingPress?: (profile: ExtendedProfile) => void, - onFollowersPress?: (profile: ExtendedProfile) => void, - onProfileImagePress?: (publication: ExtendedPublication) => void, - onCollectPress?: (publication: ExtendedPublication) => void, - onCommentPress?: (publication: ExtendedPublication) => void, - onMirrorPress?: (publication: ExtendedPublication) => void, - onLikePress?: (publication: ExtendedPublication) => void, -}) { - const HeaderComponent = ListHeaderComponent ? - ListHeaderComponent : ( - - ) - return ( - - ) -} diff --git a/src/components/Profile/Profile.tsx b/src/components/Profile/Profile.tsx new file mode 100644 index 0000000..bd54d64 --- /dev/null +++ b/src/components/Profile/Profile.tsx @@ -0,0 +1,98 @@ +import { Feed, ProfileHeader } from ".."; +import { PublicationTypes, Publication } from "../../graphql/generated"; +import { + ProfileHeaderStyles, + FeedStyles, + PublicationStyles, + FeedQuery, + ExtendedProfile, + ExtendedPublication, +} from "../../types/types"; + +export function Profile({ + profile, + ListHeaderComponent = null, + ListFooterComponent, + feed, + signedInUser, + hideLikes = false, + hideComments = false, + hideMirrors = false, + hideCollects = false, + iconColor, + infiniteScroll = true, + onEndReachedThreshold = 0.65, + headerStyles, + feedStyles, + publicationStyles, + query = { + name: "getPublications", + profileId: profile.id, + publicationTypes: [PublicationTypes.Post, PublicationTypes.Mirror], + }, + onFollowingPress = (profile) => console.log({ profile }), + onFollowersPress = (profile) => console.log({ profile }), + onProfileImagePress = (publication) => console.log({ publication }), + onCollectPress = (publication) => console.log({ publication }), + onCommentPress = (publication) => console.log({ publication }), + onMirrorPress = (publication) => console.log({ publication }), + onLikePress = (publication) => console.log({ publication }), +}: { + profile: ExtendedProfile; + ListHeaderComponent?: any; + ListFooterComponent?: React.FC<{}>; + feed?: Publication[]; + signedInUser?: any; + hideLikes?: boolean; + hideComments?: boolean; + hideMirrors?: boolean; + hideCollects?: boolean; + iconColor?: string; + infiniteScroll?: boolean; + onEndReachedThreshold?: number; + headerStyles?: ProfileHeaderStyles; + feedStyles?: FeedStyles; + publicationStyles?: PublicationStyles; + query?: FeedQuery; + onFollowingPress?: (profile: ExtendedProfile) => void; + onFollowersPress?: (profile: ExtendedProfile) => void; + onProfileImagePress?: (publication: ExtendedPublication) => void; + onCollectPress?: (publication: ExtendedPublication) => void; + onCommentPress?: (publication: ExtendedPublication) => void; + onMirrorPress?: (publication: ExtendedPublication) => void; + onLikePress?: (publication: ExtendedPublication) => void; +}) { + const HeaderComponent = ListHeaderComponent ? ( + ListHeaderComponent + ) : ( + + ); + return ( + + ); +} diff --git a/src/components/Profile/ProfileHeader.tsx b/src/components/Profile/ProfileHeader.tsx new file mode 100644 index 0000000..db87b69 --- /dev/null +++ b/src/components/Profile/ProfileHeader.tsx @@ -0,0 +1,256 @@ +import { useState, useEffect, useContext } from "react"; +import { + View, + TouchableHighlight, + StyleSheet, + Image, + Text, +} from "react-native"; +import { createClient } from "../api"; +import { ProfileDocument } from "../graphql/generated"; +import { + ExtendedProfile, + ProfileHeaderStyles, + Environment, + Theme, + LensContextType, + ThemeColors, +} from "../types/types"; +import { LensContext } from "../context/context"; + +export function ProfileHeader({ + profileId, + profile: user, + onFollowingPress = (profile) => console.log({ profile }), + onFollowersPress = (profile) => console.log({ profile }), + styles = baseStyles, +}: { + profileId?: number; + profile?: ExtendedProfile; + onFollowingPress: (profile: ExtendedProfile) => void; + onFollowersPress: (profile: ExtendedProfile) => void; + styles?: ProfileHeaderStyles; +}) { + const [fetchedProfile, setFetchedProfile] = useState(null); + const { environment, theme } = useContext(LensContext) as LensContextType; + const client = createClient(environment); + if (theme) { + if (theme === "dark") { + styles = darkThemeStyles; + } + } + useEffect(() => { + if (!profile) { + fetchProfile(); + } + }); + async function fetchProfile() { + console.log("FETCHING PROFILE"); + try { + const { data } = await client + .query(ProfileDocument, { + request: { + profileId, + }, + }) + .toPromise(); + if (data) { + const { profile: userProfile } = data; + setFetchedProfile(userProfile); + } + } catch (err) { + console.log("error fetching profile: ", err); + } + } + if (!user && !fetchedProfile) return null; + const profile = user || fetchedProfile; + let { picture, coverPicture } = profile; + if (picture && picture.original) { + if (picture.original.url.startsWith("ipfs://")) { + let result = picture.original.url.substring( + 7, + picture.original.url.length + ); + profile.picture.original.url = `https://lens.infura-ipfs.io/ipfs/${result}`; + } + } else { + profile.missingAvatar = true; + } + if (coverPicture && coverPicture.original.url) { + if (coverPicture.original.url.startsWith("ipfs://")) { + let hash = coverPicture.original.url.substring( + 7, + coverPicture.original.url.length + ); + profile.coverPicture.original.url = `https://lens.infura-ipfs.io/ipfs/${hash}`; + } + } else { + profile.missingCover = true; + } + + return ( + + {profile.missingCover ? ( + + ) : ( + + )} + {profile.missingAvatar ? ( + + ) : ( + + )} + + {profile.name} + @{profile.handle} + {profile.bio} + + onFollowingPress(profile)} + underlayColor="transparent" + > + + + {profile.stats.totalFollowing} + + Following + + + onFollowersPress(profile)} + underlayColor="transparent" + > + + + {profile.stats.totalFollowers} + + Followers + + + + + + ); +} + +const baseStyles = StyleSheet.create({ + container: {}, + blankHeader: { + height: 120, + backgroundColor: "black", + }, + headerImage: { + width: "100%", + height: 120, + }, + avatar: { + width: 100, + height: 100, + borderRadius: 50, + marginTop: -50, + marginLeft: 25, + }, + userDetails: { + paddingHorizontal: 25, + paddingVertical: 10, + }, + name: { + fontWeight: "600", + fontSize: 20, + }, + handle: { + fontSize: 14, + }, + bio: { + marginTop: 10, + color: "rgba(0, 0, 0, .5)", + }, + profileStats: { + flexDirection: "row", + marginTop: 15, + }, + statsData: { + fontWeight: "600", + fontSize: 16, + }, + statsHeader: { + marginLeft: 3, + opacity: 0.7, + }, + profileFollowingData: { + flexDirection: "row", + alignItems: "center", + }, + profileFollowerData: { + marginLeft: 15, + flexDirection: "row", + alignItems: "center", + }, +}); + +const darkThemeStyles = StyleSheet.create({ + container: { + backgroundColor: ThemeColors.black, + paddingBottom: 10, + }, + blankHeader: { + height: 120, + backgroundColor: "black", + }, + headerImage: { + width: "100%", + height: 120, + }, + avatar: { + width: 100, + height: 100, + borderRadius: 50, + marginTop: -50, + marginLeft: 25, + }, + userDetails: { + paddingHorizontal: 25, + paddingVertical: 10, + }, + name: { + fontWeight: "600", + fontSize: 20, + color: ThemeColors.white, + }, + handle: { + fontSize: 14, + color: ThemeColors.lightGray, + }, + bio: { + marginTop: 10, + color: "white", + }, + profileStats: { + flexDirection: "row", + marginTop: 15, + }, + statsData: { + fontWeight: "600", + fontSize: 16, + color: ThemeColors.lightGray, + }, + statsHeader: { + marginLeft: 3, + color: ThemeColors.white, + }, + profileFollowingData: { + flexDirection: "row", + alignItems: "center", + }, + profileFollowerData: { + marginLeft: 15, + flexDirection: "row", + alignItems: "center", + }, +}); diff --git a/src/components/Profile/ProfileListItem.tsx b/src/components/Profile/ProfileListItem.tsx new file mode 100644 index 0000000..f2c796e --- /dev/null +++ b/src/components/Profile/ProfileListItem.tsx @@ -0,0 +1,231 @@ +import { + TouchableHighlight, + View, + Image, + Text, + StyleSheet, +} from "react-native"; +import { useContext } from "react"; +import { + ProfileListItemStyles, + ExtendedProfile, + ThemeColors, + LensContextType, +} from "../types/types"; +import { LensContext } from "../context/context"; + +export function ProfileListItem({ + profile, + onProfilePress, + onFollowPress, + isFollowing, + styles = baseStyles, +}: { + profile: ExtendedProfile; + onProfilePress: any; + onFollowPress: any; + isFollowing?: boolean; + styles?: ProfileListItemStyles; +}) { + const { theme } = useContext(LensContext) as LensContextType; + if (theme) { + if (theme === "dark") { + styles = darkThemeStyles; + } + } + function renderFollowButton(isFollowing: boolean = false) { + if (isFollowing) { + return ( + + Following + + ); + } else { + return ( + + Follow + + ); + } + } + + return ( + onProfilePress(profile)} + underlayColor="transparent" + key={profile.id} + > + + + + + + + {profile.name || profile.handle} + + @{profile.handle} + + {profile.bio && profile.bio.substring(0, 150)} + + + + onFollowPress(profile)} + activeOpacity={0.6} + > + {renderFollowButton(isFollowing)} + + + + + ); +} + +const baseStyles = StyleSheet.create({ + container: { + flexDirection: "row", + paddingLeft: 15, + paddingVertical: 10, + borderBottomWidth: 1, + borderBottomColor: "rgba(0, 0, 0, .06)", + }, + avatarContainer: { + padding: 5, + }, + avatar: { + width: 44, + height: 44, + borderRadius: 22, + }, + profileName: { + fontWeight: "600", + fontSize: 16, + maxWidth: 200, + }, + profileHandle: { + marginTop: 3, + axWidth: 200, + }, + profileBio: { + maxWidth: 200, + marginTop: 15, + color: "rgba(0, 0, 0, .5)", + }, + infoContainer: { + justifyContent: "center", + paddingLeft: 10, + maxWidth: 200, + }, + followButtonContainer: { + flex: 1, + alignItems: "flex-end", + paddingRight: 20, + }, + followButton: { + borderWidth: 1, + borderRadius: 34, + paddingHorizontal: 17, + paddingVertical: 7, + marginTop: 3, + backgroundColor: "black", + }, + followingButton: { + borderWidth: 1, + borderRadius: 34, + paddingHorizontal: 17, + paddingVertical: 7, + marginTop: 3, + }, + followButtonText: { + fontSize: 12, + fontWeight: "700", + color: "white", + }, + followingButtonText: { + fontSize: 12, + fontWeight: "700", + color: "black", + }, +}); + +const darkThemeStyles = StyleSheet.create({ + container: { + flexDirection: "row", + paddingLeft: 15, + paddingVertical: 12, + backgroundColor: ThemeColors.black, + borderBottomColor: ThemeColors.clearWhite, + borderBottomWidth: 1, + }, + avatarContainer: { + padding: 5, + }, + avatar: { + width: 44, + height: 44, + borderRadius: 22, + }, + profileName: { + fontWeight: "600", + fontSize: 16, + maxWidth: 200, + color: ThemeColors.white, + }, + profileHandle: { + marginTop: 3, + axWidth: 200, + color: ThemeColors.lightGray, + }, + profileBio: { + maxWidth: 200, + marginTop: 15, + color: ThemeColors.white, + }, + infoContainer: { + justifyContent: "center", + paddingLeft: 10, + maxWidth: 200, + }, + followButtonContainer: { + flex: 1, + alignItems: "flex-end", + paddingRight: 20, + }, + followButton: { + borderWidth: 1, + borderRadius: 34, + paddingHorizontal: 17, + paddingVertical: 7, + marginTop: 3, + backgroundColor: ThemeColors.white, + color: ThemeColors.black, + }, + followingButton: { + borderWidth: 1, + borderRadius: 34, + paddingHorizontal: 17, + paddingVertical: 7, + marginTop: 3, + borderColor: ThemeColors.white, + }, + followButtonText: { + fontSize: 12, + fontWeight: "700", + color: ThemeColors.black, + }, + followingButtonText: { + fontSize: 12, + fontWeight: "700", + color: ThemeColors.white, + }, +}); diff --git a/src/components/Profile/Profiles.tsx b/src/components/Profile/Profiles.tsx new file mode 100644 index 0000000..5bcffe1 --- /dev/null +++ b/src/components/Profile/Profiles.tsx @@ -0,0 +1,262 @@ +import { useState, useEffect, useContext } from "react"; +import { FlatList, ActivityIndicator, StyleSheet } from "react-native"; +import { createClient } from "../api"; +import { + ProfilesQuery, + ExtendedProfile, + Environment, + LensContextType, +} from "../types/types"; +import { + Profile, + ExploreProfilesDocument, + FollowingDocument, + ProfileSortCriteria, + PaginatedResultInfo, + DoesFollowDocument, +} from "../graphql/generated"; +import { ProfileListItem } from "./"; +import { LensContext } from "../context/context"; + +export function Profiles({ + onFollowPress = (profile) => console.log({ profile }), + onProfilePress = (profile) => console.log({ profile }), + profileData, + onEndReachedThreshold = 0.7, + infiniteScroll = true, + signedInUserAddress, + query = { + name: "exploreProfiles", + sortCriteria: ProfileSortCriteria.MostFollowers, + limit: 25, + }, +}: { + onFollowPress?: ( + profile: ExtendedProfile, + profiles: ExtendedProfile[] + ) => void; + onProfilePress?: (profile: ExtendedProfile) => void; + profileData?: ExtendedProfile[]; + onEndReachedThreshold?: number; + infiniteScroll?: boolean; + query?: ProfilesQuery; + signedInUserAddress?: string; +}) { + const [profiles, setProfiles] = useState([]); + const [loading, setLoading] = useState(true); + const [canPaginate, setCanPaginate] = useState(true); + const [paginationInfo, setPaginationInfo] = useState< + PaginatedResultInfo | undefined + >(); + const { environment } = useContext(LensContext) as LensContextType; + const client = createClient(environment); + + useEffect(() => { + fetchProfiles(); + }, []); + + async function fetchResponse(cursor = null) { + if (query.name === "exploreProfiles") { + try { + let { data } = await client + .query(ExploreProfilesDocument, { + request: { + sortCriteria: + query.sortCriteria || ProfileSortCriteria.MostFollowers, + cursor, + limit: query.limit, + }, + }) + .toPromise(); + if (data && data.exploreProfiles) { + let { + exploreProfiles: { pageInfo, items }, + } = data; + if (signedInUserAddress) { + const requestData = items.map((i) => ({ + followerAddress: signedInUserAddress, + profileId: i.id, + })); + const response = await client + .query(DoesFollowDocument, { + request: { + followInfos: requestData, + }, + }) + .toPromise(); + items = items.map((item, index) => { + item.isFollowing = + response?.data?.doesFollow[index].follows || false; + return item; + }); + } + return { + pageInfo, + items, + }; + } + } catch (err) { + console.log("Error fetching profiles: ", err); + setLoading(false); + } + } + if (query.name === "getFollowing") { + let { data } = await client + .query(FollowingDocument, { + request: { + address: query.ethereumAddress, + cursor, + limit: query.limit || 25, + }, + }) + .toPromise(); + if (data) { + let { following } = data; + let { + pageInfo, + items, + }: { + pageInfo: PaginatedResultInfo; + items: any; + } = following; + if (signedInUserAddress) { + const requestData = items.map((i: any) => ({ + followerAddress: signedInUserAddress, + profileId: i.profile.id, + })); + const response = await client + .query(DoesFollowDocument, { + request: { + followInfos: requestData, + }, + }) + .toPromise(); + items = items.map((item: any, index: number) => { + item.profile.isFollowing = + response?.data?.doesFollow[index].follows; + return item.profile; + }); + } else { + items = items.map((item: any) => { + return item.profile; + }); + } + return { + pageInfo, + items, + }; + } + } + } + + async function fetchProfiles(cursor = null) { + if (profileData) { + setProfiles(profileData); + return; + } + try { + if (!profileData || (profileData && profiles.length)) { + setLoading(true); + let { items, pageInfo } = (await fetchResponse(cursor)) as { + items: ExtendedProfile[]; + pageInfo: PaginatedResultInfo; + }; + setPaginationInfo(pageInfo); + items = await Promise.all( + items.map((profile) => { + let { picture, coverPicture } = profile; + if (picture && picture.__typename === "MediaSet") { + if (picture.original) { + if (picture.original.url.startsWith("ipfs://")) { + let result = picture.original.url.substring( + 7, + picture.original.url.length + ); + picture.original.url = `https://lens.infura-ipfs.io/ipfs/${result}`; + } + } else { + profile.missingAvatar = true; + } + } + if (coverPicture && coverPicture.__typename === "MediaSet") { + if (coverPicture.original.url) { + if (coverPicture.original.url.startsWith("ipfs://")) { + let hash = coverPicture.original.url.substring( + 7, + coverPicture.original.url.length + ); + coverPicture.original.url = `https://lens.infura-ipfs.io/ipfs/${hash}`; + } + } else { + profile.missingCover = true; + } + } + return profile; + }) + ); + setLoading(false); + setProfiles([...profiles, ...items]); + } else { + setProfiles(profileData); + } + } catch (err) { + console.log("Error fetching profiles... ", err); + setLoading(false); + } + } + + function onEndReached() { + if (infiniteScroll) { + fetchNextItems(); + } + } + + async function fetchNextItems() { + try { + if (canPaginate && paginationInfo) { + const { next } = paginationInfo; + if (!next) { + setCanPaginate(false); + } else { + fetchProfiles(next); + } + } + } catch (err) { + console.log("Error fetching next items: ", err); + } + } + + function renderItem({ item, index }: { item: Profile; index: number }) { + return ( + + onFollowPress(profile, profiles) + } + isFollowing={item.isFollowing} + /> + ); + } + + return ( + + ) : null + } + /> + ); +} + +const styles = StyleSheet.create({ + loadingIndicatorStyle: { + marginVertical: 20, + }, +}); diff --git a/src/components/ProfileHeader.tsx b/src/components/ProfileHeader.tsx deleted file mode 100644 index 7b21e47..0000000 --- a/src/components/ProfileHeader.tsx +++ /dev/null @@ -1,246 +0,0 @@ -import { useState, useEffect, useContext } from 'react' -import { - View, TouchableHighlight, StyleSheet, Image, Text -} from 'react-native' -import { createClient } from '../api' -import { ProfileDocument } from '../graphql/generated' -import { - ExtendedProfile, - ProfileHeaderStyles, - Environment, - Theme, - LensContextType, - ThemeColors -} from '../types' -import { LensContext } from '../context' - -export function ProfileHeader({ - profileId, - profile: user, - onFollowingPress = profile => console.log({ profile }), - onFollowersPress = profile => console.log({ profile }), - styles = baseStyles, -}: { - profileId?: number, - profile?: ExtendedProfile, - onFollowingPress: (profile: ExtendedProfile) => void, - onFollowersPress: (profile: ExtendedProfile) => void, - styles?: ProfileHeaderStyles -}) { - const [fetchedProfile, setFetchedProfile] = useState(null) - const { environment, theme } = useContext(LensContext) as LensContextType - const client = createClient(environment) - if (theme) { - if (theme === 'dark') { - styles = darkThemeStyles - } - } - useEffect(() => { - if (!profile) { - fetchProfile() - } - }) - async function fetchProfile() { - console.log("FETCHING PROFILE") - try { - const { data } = await client.query(ProfileDocument, { - request: { - profileId - } - }).toPromise() - if (data) { - const { profile: userProfile } = data - setFetchedProfile(userProfile) - } - } catch (err) { - console.log('error fetching profile: ', err) - } - } - if (!user && !fetchedProfile) return null - const profile = user || fetchedProfile - let { picture, coverPicture } = profile - if (picture && picture.original) { - if (picture.original.url.startsWith('ipfs://')) { - let result = picture.original.url.substring(7, picture.original.url.length) - profile.picture.original.url = `https://lens.infura-ipfs.io/ipfs/${result}` - } - } else { - profile.missingAvatar = true - } - if (coverPicture && coverPicture.original.url) { - if (coverPicture.original.url.startsWith('ipfs://')) { - let hash = coverPicture.original.url.substring(7, coverPicture.original.url.length) - profile.coverPicture.original.url = `https://lens.infura-ipfs.io/ipfs/${hash}` - } - } else { - profile.missingCover = true - } - - return ( - - { - profile.missingCover ? ( - - ) : ( - - ) - } - { - profile.missingAvatar ? ( - - ) : ( - - ) - } - - {profile.name} - @{profile.handle} - {profile.bio} - - onFollowingPress(profile)} - underlayColor="transparent" - > - - {profile.stats.totalFollowing} - Following - - - onFollowersPress(profile)} - underlayColor="transparent" - > - - {profile.stats.totalFollowers} - Followers - - - - - - ) -} - -const baseStyles = StyleSheet.create({ - container: {}, - blankHeader: { - height: 120, - backgroundColor: 'black' - }, - headerImage: { - width: '100%', - height: 120 - }, - avatar: { - width: 100, - height: 100, - borderRadius: 50, - marginTop: -50, - marginLeft: 25 - }, - userDetails: { - paddingHorizontal: 25, - paddingVertical: 10 - }, - name: { - fontWeight: '600', - fontSize: 20, - }, - handle: { - fontSize: 14, - }, - bio: { - marginTop: 10, - color: 'rgba(0, 0, 0, .5)' - }, - profileStats: { - flexDirection: 'row', - marginTop: 15 - }, - statsData: { - fontWeight: '600', - fontSize: 16, - }, - statsHeader: { - marginLeft: 3, - opacity: .7 - }, - profileFollowingData: { - flexDirection: 'row', - alignItems: 'center' - }, - profileFollowerData: { - marginLeft: 15, - flexDirection: 'row', - alignItems: 'center' - } -}) - -const darkThemeStyles = StyleSheet.create({ - container: { - backgroundColor: ThemeColors.black, - paddingBottom: 10 - }, - blankHeader: { - height: 120, - backgroundColor: 'black' - }, - headerImage: { - width: '100%', - height: 120 - }, - avatar: { - width: 100, - height: 100, - borderRadius: 50, - marginTop: -50, - marginLeft: 25 - }, - userDetails: { - paddingHorizontal: 25, - paddingVertical: 10 - }, - name: { - fontWeight: '600', - fontSize: 20, - color: ThemeColors.white - }, - handle: { - fontSize: 14, - color: ThemeColors.lightGray - }, - bio: { - marginTop: 10, - color: 'white' - }, - profileStats: { - flexDirection: 'row', - marginTop: 15 - }, - statsData: { - fontWeight: '600', - fontSize: 16, - color: ThemeColors.lightGray - }, - statsHeader: { - marginLeft: 3, - color: ThemeColors.white - }, - profileFollowingData: { - flexDirection: 'row', - alignItems: 'center' - }, - profileFollowerData: { - marginLeft: 15, - flexDirection: 'row', - alignItems: 'center' - } -}) \ No newline at end of file diff --git a/src/components/ProfileListItem.tsx b/src/components/ProfileListItem.tsx deleted file mode 100644 index 1b5931a..0000000 --- a/src/components/ProfileListItem.tsx +++ /dev/null @@ -1,230 +0,0 @@ -import { - TouchableHighlight, View, Image, Text, StyleSheet -} from 'react-native' -import { useContext } from 'react' -import { - ProfileListItemStyles, - ExtendedProfile, - ThemeColors, - LensContextType -} from '../types' -import { LensContext } from '../context' - -export function ProfileListItem({ - profile, - onProfilePress, - onFollowPress, - isFollowing, - styles = baseStyles -} : { - profile: ExtendedProfile, - onProfilePress: any, - onFollowPress: any, - isFollowing?: boolean, - styles?: ProfileListItemStyles -}) { - const { theme } = useContext(LensContext) as LensContextType - if (theme) { - if (theme === 'dark') { - styles = darkThemeStyles - } - } - function renderFollowButton(isFollowing: boolean = false) { - if (isFollowing) { - return ( - - - Following - - - ) - } else { - return ( - - - Follow - - - ) - } - } - - return ( - onProfilePress(profile)} - underlayColor="transparent" - key={profile.id} - > - - - - - - {profile.name || profile.handle} - @{profile.handle} - {profile.bio && profile.bio.substring(0, 150)} - - - - onFollowPress(profile)} - activeOpacity={0.6} - > - { - renderFollowButton(isFollowing) - } - - - - - ) -} - -const baseStyles = StyleSheet.create({ - container: { - flexDirection: 'row', - paddingLeft: 15, - paddingVertical: 10, - borderBottomWidth: 1, - borderBottomColor: 'rgba(0, 0, 0, .06)' - }, - avatarContainer: { - padding: 5 - }, - avatar: { - width: 44, - height: 44, - borderRadius: 22 - }, - profileName: { - fontWeight: '600', - fontSize: 16, - maxWidth: 200, - - }, - profileHandle: { - marginTop: 3, - axWidth: 200 - }, - profileBio: { - maxWidth: 200, - marginTop: 15, - color: 'rgba(0, 0, 0, .5)' - }, - infoContainer: { - justifyContent: 'center', - paddingLeft: 10, - maxWidth: 200, - }, - followButtonContainer: { - flex: 1, - alignItems: 'flex-end', - paddingRight: 20 - }, - followButton: { - borderWidth: 1, - borderRadius: 34, - paddingHorizontal: 17, - paddingVertical:7, - marginTop: 3, - backgroundColor: 'black', - }, - followingButton: { - borderWidth: 1, - borderRadius: 34, - paddingHorizontal: 17, - paddingVertical:7, - marginTop: 3, - }, - followButtonText: { - fontSize: 12, - fontWeight: '700', - color: 'white' - }, - followingButtonText: { - fontSize: 12, - fontWeight: '700', - color: 'black' - } -}) - -const darkThemeStyles = StyleSheet.create({ - container: { - flexDirection: 'row', - paddingLeft: 15, - paddingVertical: 12, - backgroundColor: ThemeColors.black, - borderBottomColor: ThemeColors.clearWhite, - borderBottomWidth: 1 - }, - avatarContainer: { - padding: 5 - }, - avatar: { - width: 44, - height: 44, - borderRadius: 22 - }, - profileName: { - fontWeight: '600', - fontSize: 16, - maxWidth: 200, - color: ThemeColors.white - }, - profileHandle: { - marginTop: 3, - axWidth: 200, - color: ThemeColors.lightGray - }, - profileBio: { - maxWidth: 200, - marginTop: 15, - color: ThemeColors.white - }, - infoContainer: { - justifyContent: 'center', - paddingLeft: 10, - maxWidth: 200, - }, - followButtonContainer: { - flex: 1, - alignItems: 'flex-end', - paddingRight: 20 - }, - followButton: { - borderWidth: 1, - borderRadius: 34, - paddingHorizontal: 17, - paddingVertical:7, - marginTop: 3, - backgroundColor: ThemeColors.white, - color: ThemeColors.black - }, - followingButton: { - borderWidth: 1, - borderRadius: 34, - paddingHorizontal: 17, - paddingVertical:7, - marginTop: 3, - borderColor: ThemeColors.white - }, - followButtonText: { - fontSize: 12, - fontWeight: '700', - color: ThemeColors.black - }, - followingButtonText: { - fontSize: 12, - fontWeight: '700', - color: ThemeColors.white - } -}) - diff --git a/src/components/Profiles.tsx b/src/components/Profiles.tsx deleted file mode 100644 index 032706d..0000000 --- a/src/components/Profiles.tsx +++ /dev/null @@ -1,243 +0,0 @@ -import { useState, useEffect, useContext } from 'react' -import { - FlatList, ActivityIndicator, StyleSheet -} from 'react-native' -import { createClient } from '../api' -import { - ProfilesQuery, - ExtendedProfile, - Environment, - LensContextType -} from '../types' -import { - Profile, - ExploreProfilesDocument, - FollowingDocument, - ProfileSortCriteria, - PaginatedResultInfo, - DoesFollowDocument -} from '../graphql/generated' -import { - ProfileListItem -} from './' -import { LensContext } from '../context' - -export function Profiles({ - onFollowPress = profile => console.log({ profile }), - onProfilePress = profile => console.log({ profile }), - profileData, - onEndReachedThreshold = .7, - infiniteScroll = true, - signedInUserAddress, - query = { - name: 'exploreProfiles', - sortCriteria: ProfileSortCriteria.MostFollowers, - limit: 25 - } -} : { - onFollowPress?: (profile: ExtendedProfile, profiles: ExtendedProfile[]) => void, - onProfilePress?: (profile: ExtendedProfile) => void, - profileData?: ExtendedProfile[], - onEndReachedThreshold?: number, - infiniteScroll?: boolean, - query?: ProfilesQuery, - signedInUserAddress?: string -}) { - const [profiles, setProfiles] = useState([]) - const [loading, setLoading] = useState(true) - const [canPaginate, setCanPaginate] = useState(true) - const [paginationInfo, setPaginationInfo] = useState() - const { environment } = useContext(LensContext) as LensContextType - const client = createClient(environment) - - useEffect(() => { - fetchProfiles() - }, []) - - async function fetchResponse(cursor = null) { - if (query.name === 'exploreProfiles') { - try { - let { data } = await client.query(ExploreProfilesDocument, { - request: { - sortCriteria: query.sortCriteria || ProfileSortCriteria.MostFollowers, - cursor, - limit: query.limit - } - }).toPromise() - if (data && data.exploreProfiles) { - let { exploreProfiles: { pageInfo, items } } = data - if (signedInUserAddress) { - const requestData = items.map(i => ({ - followerAddress: signedInUserAddress, - profileId: i.id - })) - const response = await client.query(DoesFollowDocument, { - request: { - followInfos: requestData - } - }).toPromise() - items = items.map((item, index) => { - item.isFollowing = response?.data?.doesFollow[index].follows || false - return item - }) - } - return { - pageInfo, items, - } - } - } catch (err) { - console.log('Error fetching profiles: ', err) - setLoading(false) - } - } - if (query.name === 'getFollowing') { - let { data } = await client.query(FollowingDocument, { - request: { - address: query.ethereumAddress, - cursor, - limit: query.limit || 25 - } - }).toPromise() - if (data) { - let { following } = data - let { pageInfo, items } : { - pageInfo: PaginatedResultInfo, - items: any - } = following - if (signedInUserAddress) { - const requestData = items.map((i: any) => ({ - followerAddress: signedInUserAddress, - profileId: i.profile.id - })) - const response = await client.query(DoesFollowDocument, { - request: { - followInfos: requestData - } - }).toPromise() - items = items.map((item: any, index: number) => { - item.profile.isFollowing = response?.data?.doesFollow[index].follows - return item.profile - }) - } else { - items = items.map((item: any) => { - return item.profile - }) - } - return { - pageInfo, items - } - } - } - } - - async function fetchProfiles(cursor=null) { - if (profileData) { - setProfiles(profileData) - return - } - try { - if ( - !profileData || - profileData && profiles.length - ) { - setLoading(true) - let { - items, pageInfo - } = await fetchResponse(cursor) as { - items: ExtendedProfile[], pageInfo: PaginatedResultInfo - } - setPaginationInfo(pageInfo) - items = await Promise.all(items.map(profile => { - let { picture, coverPicture } = profile - if (picture && picture.__typename === 'MediaSet') { - if (picture.original) { - if (picture.original.url.startsWith('ipfs://')) { - let result = picture.original.url.substring(7, picture.original.url.length) - picture.original.url = `https://lens.infura-ipfs.io/ipfs/${result}` - } - } else { - profile.missingAvatar = true - } - } - if (coverPicture && coverPicture.__typename === 'MediaSet') { - if (coverPicture.original.url) { - if (coverPicture.original.url.startsWith('ipfs://')) { - let hash = coverPicture.original.url.substring(7, coverPicture.original.url.length) - coverPicture.original.url = `https://lens.infura-ipfs.io/ipfs/${hash}` - } - } else { - profile.missingCover = true - } - } - return profile - }) - ) - setLoading(false) - setProfiles([...profiles, ...items]) - } else { - setProfiles(profileData) - } - } catch (err) { - console.log("Error fetching profiles... ", err) - setLoading(false) - } - } - - function onEndReached() { - if (infiniteScroll) { - fetchNextItems() - } - } - - async function fetchNextItems() { - try { - if (canPaginate && paginationInfo) { - const { next } = paginationInfo - if (!next) { - setCanPaginate(false) - } else { - fetchProfiles(next) - } - } - } catch (err) { - console.log('Error fetching next items: ', err) - } - } - - function renderItem({ item, index } : { - item: Profile, - index: number - }) { - return ( - onFollowPress(profile, profiles)} - isFollowing={item.isFollowing} - /> - ) - } - - return ( - - ) : null - } - /> - ) -} - -const styles = StyleSheet.create({ - loadingIndicatorStyle: { - marginVertical: 20 - } -}) \ No newline at end of file diff --git a/src/components/Publication.tsx b/src/components/Publication.tsx index 6105053..468b3b9 100644 --- a/src/components/Publication.tsx +++ b/src/components/Publication.tsx @@ -4,21 +4,27 @@ import { Text, Dimensions, Image, - TouchableHighlight -} from 'react-native' -import { useContext } from 'react' -import { formatDistanceStrict } from 'date-fns' + TouchableHighlight, +} from "react-native"; +import { useContext } from "react"; +import { formatDistanceStrict } from "date-fns"; import { PublicationStyles, ExtendedPublication, LensContextType, - ThemeColors -} from '../types' -import { returnIPFSPathorURL } from '../utils' -import { LensContext } from '../context' -import { CommentIcon, MirrorIcon, CollectIcon, UnfilledHeartIcon, FilledHeartIcon } from './' + ThemeColors, +} from "../types/types"; +import { returnIPFSPathorURL } from "../utils/utils"; +import { LensContext } from "../context/context"; +import { + CommentIcon, + MirrorIcon, + CollectIcon, + UnfilledHeartIcon, + FilledHeartIcon, +} from "./"; -const width = Dimensions.get('window').width +const width = Dimensions.get("window").width; export function Publication({ publication, @@ -28,183 +34,173 @@ export function Publication({ hideMirrors = false, hideCollects = false, iconColor, - onCollectPress = publication => console.log({ publication }), - onCommentPress = publication => console.log({ publication }), - onMirrorPress= publication => console.log({ publication }), - onLikePress = publication => console.log({ publication }), - onProfileImagePress = publication => console.log({ publication }), - styles = baseStyles + onCollectPress = (publication) => console.log({ publication }), + onCommentPress = (publication) => console.log({ publication }), + onMirrorPress = (publication) => console.log({ publication }), + onLikePress = (publication) => console.log({ publication }), + onProfileImagePress = (publication) => console.log({ publication }), + styles = baseStyles, }: { - publication: ExtendedPublication, - signedInUser: any, - hideLikes: boolean, - hideComments: boolean, - hideMirrors: boolean, - hideCollects: boolean, - iconColor?: string, - onCollectPress: (publication: ExtendedPublication) => void, - onCommentPress:(publication: ExtendedPublication) => void, - onMirrorPress: (publication: ExtendedPublication) => void, - onLikePress: (publication: ExtendedPublication) => void, - onProfileImagePress: (publication: ExtendedPublication) => void, - styles?: PublicationStyles + publication: ExtendedPublication; + signedInUser: any; + hideLikes: boolean; + hideComments: boolean; + hideMirrors: boolean; + hideCollects: boolean; + iconColor?: string; + onCollectPress: (publication: ExtendedPublication) => void; + onCommentPress: (publication: ExtendedPublication) => void; + onMirrorPress: (publication: ExtendedPublication) => void; + onLikePress: (publication: ExtendedPublication) => void; + onProfileImagePress: (publication: ExtendedPublication) => void; + styles?: PublicationStyles; }) { - const { theme } = useContext(LensContext) as LensContextType + const { theme } = useContext(LensContext) as LensContextType; if (theme) { - if (theme === 'dark') { - styles = darkThemeStyles - iconColor = ThemeColors.lightGray + if (theme === "dark") { + styles = darkThemeStyles; + iconColor = ThemeColors.lightGray; } } return ( - - + + onProfileImagePress(publication)} > - { - publication?.profile?.picture?.__typename !== 'MediaSet' || publication.profile.missingAvatar ? ( - + {publication?.profile?.picture?.__typename !== "MediaSet" || + publication.profile.missingAvatar ? ( + ) : ( - ) - } + )} - { - publication.__typename === 'Mirror' && publication.mirrorOf && ( - - - Mirrored by {publication?.originalProfile?.name} - - ) - } + {publication.__typename === "Mirror" && publication.mirrorOf && ( + + + + Mirrored by {publication?.originalProfile?.name} + + + )} {publication.profile.name} - @{publication.profile.handle} - • {reduceDate(publication.createdAt)} + + @{publication.profile.handle} + + + • {reduceDate(publication.createdAt)} + - { - publication.metadata.content && ( - {publication.metadata.content} - ) - } - { - Number(publication.metadata.media.length) > 0 && ( - - ) - } + {publication.metadata.content && ( + {publication.metadata.content} + )} + {Number(publication.metadata.media.length) > 0 && ( + + )} - { - publication.stats && ( - - { - !hideComments && ( - - onCommentPress(publication)} - color={iconColor} - /> - {publication.stats.totalAmountOfComments} - - ) - } - { - !hideMirrors && ( - - onMirrorPress(publication)} - color={iconColor} - /> - {publication.stats.totalAmountOfMirrors} - - ) - } - { - !hideCollects && ( - - onCollectPress(publication)} - color={iconColor} - /> - {publication.stats.totalAmountOfCollects} - - ) - } - { - !signedInUser && !hideLikes && ( - - onLikePress(publication)} - color={iconColor} - /> - {publication.stats.totalUpvotes} - - ) - } - { - signedInUser && !hideLikes && ( - - onLikePress(publication)} - color={iconColor} - /> - {publication.stats.totalUpvotes} - - ) - } - - ) - } + {publication.stats && ( + + {!hideComments && ( + + onCommentPress(publication)} + color={iconColor} + /> + + {publication.stats.totalAmountOfComments} + + + )} + {!hideMirrors && ( + + onMirrorPress(publication)} + color={iconColor} + /> + + {publication.stats.totalAmountOfMirrors} + + + )} + {!hideCollects && ( + + onCollectPress(publication)} + color={iconColor} + /> + + {publication.stats.totalAmountOfCollects} + + + )} + {!signedInUser && !hideLikes && ( + + onLikePress(publication)} + color={iconColor} + /> + + {publication.stats.totalUpvotes} + + + )} + {signedInUser && !hideLikes && ( + + onLikePress(publication)} + color={iconColor} + /> + + {publication.stats.totalUpvotes} + + + )} + + )} - ) + ); } function reduceDate(date: any) { - const formattedDate = formatDistanceStrict(new Date(date), new Date()) - const dateArr = formattedDate.split(' ') - const dateInfo = dateArr[1].charAt(0) - return `${dateArr[0]}${dateInfo}` + const formattedDate = formatDistanceStrict(new Date(date), new Date()); + const dateArr = formattedDate.split(" "); + const dateInfo = dateArr[1].charAt(0); + return `${dateArr[0]}${dateInfo}`; } const baseStyles = StyleSheet.create({ publicationWrapper: { borderBottomWidth: 1, - borderBottomColor: 'rgba(0, 0, 0, .05)', - padding: 20 + borderBottomColor: "rgba(0, 0, 0, .05)", + padding: 20, }, publicationContainer: { - flexDirection: 'row', + flexDirection: "row", }, missingAvatarPlaceholder: { width: 40, height: 40, borderRadius: 20, - backgroundColor: 'rgba(0, 0, 0, .4)' + backgroundColor: "rgba(0, 0, 0, .4)", }, smallAvatar: { width: 40, @@ -213,12 +209,12 @@ const baseStyles = StyleSheet.create({ }, postContentContainer: { flexShrink: 1, - paddingLeft: 15 + paddingLeft: 15, }, postText: { flexShrink: 1, marginTop: 7, - marginBottom: 5 + marginBottom: 5, }, metadataImage: { marginTop: 10, @@ -228,67 +224,67 @@ const baseStyles = StyleSheet.create({ }, statsContainer: { marginTop: 20, - flexDirection: 'row', + flexDirection: "row", paddingLeft: 20, }, statsDetailContainer: { - flexDirection: 'row', + flexDirection: "row", marginRight: 20, - alignItems: 'center' + alignItems: "center", }, statsDetailText: { marginLeft: 10, - fontSize: 12 + fontSize: 12, }, postOwnerDetailsContainer: { - flexDirection: 'row', - alignItems: 'center' + flexDirection: "row", + alignItems: "center", }, postOwnerName: { - fontWeight: '600' + fontWeight: "600", }, postOwnerHandle: { marginLeft: 4, - color: 'rgba(0, 0, 0, .5)' + color: "rgba(0, 0, 0, .5)", }, timestamp: { marginLeft: 4, - color: 'rgba(0, 0, 0, .5)', + color: "rgba(0, 0, 0, .5)", fontSize: 12, - fontWeight: '600' + fontWeight: "600", }, activityIndicatorContainer: { height: 60, - justifyContent: 'center', + justifyContent: "center", marginBottom: 10, }, mirrorContainer: { - flexDirection: 'row' + flexDirection: "row", }, mirrorText: { - fontWeight: '600', - color: 'rgba(0, 0, 0, .6)', + fontWeight: "600", + color: "rgba(0, 0, 0, .6)", fontSize: 12, marginBottom: 7, - marginLeft: 5 - } -}) + marginLeft: 5, + }, +}); const darkThemeStyles = StyleSheet.create({ publicationWrapper: { borderBottomWidth: 1, padding: 20, backgroundColor: ThemeColors.black, - borderBottomColor: ThemeColors.clearWhite + borderBottomColor: ThemeColors.clearWhite, }, publicationContainer: { - flexDirection: 'row', + flexDirection: "row", }, missingAvatarPlaceholder: { width: 40, height: 40, borderRadius: 20, - backgroundColor: 'rgba(0, 0, 0, .4)' + backgroundColor: "rgba(0, 0, 0, .4)", }, smallAvatar: { width: 40, @@ -297,13 +293,13 @@ const darkThemeStyles = StyleSheet.create({ }, postContentContainer: { flexShrink: 1, - paddingLeft: 15 + paddingLeft: 15, }, postText: { flexShrink: 1, marginTop: 7, marginBottom: 5, - color: ThemeColors.white + color: ThemeColors.white, }, metadataImage: { marginTop: 10, @@ -313,50 +309,50 @@ const darkThemeStyles = StyleSheet.create({ }, statsContainer: { marginTop: 20, - flexDirection: 'row', + flexDirection: "row", paddingLeft: 20, }, statsDetailContainer: { - flexDirection: 'row', + flexDirection: "row", marginRight: 20, - alignItems: 'center' + alignItems: "center", }, statsDetailText: { marginLeft: 10, fontSize: 12, - color: ThemeColors.white + color: ThemeColors.white, }, postOwnerDetailsContainer: { - flexDirection: 'row', - alignItems: 'center' + flexDirection: "row", + alignItems: "center", }, postOwnerName: { - fontWeight: '600', - color: ThemeColors.white + fontWeight: "600", + color: ThemeColors.white, }, postOwnerHandle: { marginLeft: 4, - color: ThemeColors.lightGray + color: ThemeColors.lightGray, }, timestamp: { marginLeft: 4, fontSize: 12, - fontWeight: '600', - color: ThemeColors.lightGray + fontWeight: "600", + color: ThemeColors.lightGray, }, activityIndicatorContainer: { height: 60, - justifyContent: 'center', + justifyContent: "center", marginBottom: 10, }, mirrorContainer: { - flexDirection: 'row' + flexDirection: "row", }, mirrorText: { - fontWeight: '600', + fontWeight: "600", fontSize: 12, marginBottom: 7, marginLeft: 5, - color: ThemeColors.lightGray - } -}) \ No newline at end of file + color: ThemeColors.lightGray, + }, +}); diff --git a/src/components/index.tsx b/src/components/index.tsx index 67dd496..e0031d8 100644 --- a/src/components/index.tsx +++ b/src/components/index.tsx @@ -1,17 +1,17 @@ /* icons */ -export { FilledHeartIcon } from './Icons/FilledHeartIcon' -export { UnfilledHeartIcon } from './Icons/UnfilledHeartIcon' -export { MirrorIcon } from './Icons/MirrorIcon' -export { CollectIcon } from './Icons/CollectIcon' -export { CommentIcon } from './Icons/CommentIcon' +export { FilledHeartIcon } from "./Icons/FilledHeartIcon"; +export { UnfilledHeartIcon } from "./Icons/UnfilledHeartIcon"; +export { MirrorIcon } from "./Icons/MirrorIcon"; +export { CollectIcon } from "./Icons/CollectIcon"; +export { CommentIcon } from "./Icons/CommentIcon"; /* components */ -export { Profile } from './Profile' -export { Profiles } from './Profiles' -export { ProfileListItem } from './ProfileListItem' -export { Feed } from './Feed' -export { ProfileHeader } from './ProfileHeader' -export { Publication } from './Publication' +export { Profile } from "./Profile/Profile"; +export { Profiles } from "./Profile/Profiles"; +export { ProfileListItem } from "./Profile/ProfileListItem"; +export { Feed } from "./Feed"; +export { ProfileHeader } from "./Profile/ProfileHeader"; +export { Publication } from "./Publication"; /* provider */ -export { LensProvider } from './LensProvider' \ No newline at end of file +export { LensProvider } from "./LensProvider"; diff --git a/src/context.ts b/src/context.ts deleted file mode 100644 index 1b89192..0000000 --- a/src/context.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { createContext } from 'react' -import { LensContextType } from './types' - -export const LensContext = createContext({}) \ No newline at end of file diff --git a/src/context/context.ts b/src/context/context.ts new file mode 100644 index 0000000..72f3d74 --- /dev/null +++ b/src/context/context.ts @@ -0,0 +1,4 @@ +import { createContext } from "react"; +import { LensContextType } from "../types/types"; + +export const LensContext = createContext({}); diff --git a/src/graphql/common.graphql b/src/graphql/fragments/common.graphql similarity index 100% rename from src/graphql/common.graphql rename to src/graphql/fragments/common.graphql diff --git a/src/graphql/queries/challenge.graphql b/src/graphql/queries/challenge.graphql new file mode 100644 index 0000000..1684408 --- /dev/null +++ b/src/graphql/queries/challenge.graphql @@ -0,0 +1,5 @@ +query challenge($request: ChallengeRequest!) { + challenge(request: $request) { + text + } +} diff --git a/src/graphql/doesFollow.graphql b/src/graphql/queries/doesFollow.graphql similarity index 100% rename from src/graphql/doesFollow.graphql rename to src/graphql/queries/doesFollow.graphql diff --git a/src/graphql/exploreProfiles.graphql b/src/graphql/queries/exploreProfiles.graphql similarity index 100% rename from src/graphql/exploreProfiles.graphql rename to src/graphql/queries/exploreProfiles.graphql diff --git a/src/graphql/explorePublications.graphql b/src/graphql/queries/explorePublications.graphql similarity index 100% rename from src/graphql/explorePublications.graphql rename to src/graphql/queries/explorePublications.graphql diff --git a/src/graphql/queries/feedHilights.graphql b/src/graphql/queries/feedHilights.graphql new file mode 100644 index 0000000..2efaaa5 --- /dev/null +++ b/src/graphql/queries/feedHilights.graphql @@ -0,0 +1,23 @@ +query feedHighlights( + $request: FeedHighlightsRequest! + $reactionRequest: ReactionFieldResolverRequest + $profileId: ProfileId +) { + feedHighlights(request: $request) { + items { + ... on Post { + ...PostFields + } + ... on Comment { + ...CommentFields + } + ... on Mirror { + ...MirrorFields + } + } + pageInfo { + totalCount + next + } + } +} diff --git a/src/graphql/getFollowing.graphql b/src/graphql/queries/getFollowing.graphql similarity index 100% rename from src/graphql/getFollowing.graphql rename to src/graphql/queries/getFollowing.graphql diff --git a/src/graphql/queries/getNftsFeed.graphql b/src/graphql/queries/getNftsFeed.graphql new file mode 100644 index 0000000..b8942be --- /dev/null +++ b/src/graphql/queries/getNftsFeed.graphql @@ -0,0 +1,19 @@ +query nftFeed($request: NFTsRequest!) { + nfts(request: $request) { + items { + name + collectionName + contractAddress + tokenId + chainId + originalContent { + uri + animatedUrl + } + } + pageInfo { + next + totalCount + } + } +} diff --git a/src/graphql/queries/getNotifications.graphql b/src/graphql/queries/getNotifications.graphql new file mode 100644 index 0000000..5ef1174 --- /dev/null +++ b/src/graphql/queries/getNotifications.graphql @@ -0,0 +1,146 @@ +query notifications($request: NotificationRequest!) { + notifications(request: $request) { + items { + ... on NewFollowerNotification { + notificationId + wallet { + address + defaultProfile { + ...ProfileFields + } + } + createdAt + } + ... on NewMentionNotification { + notificationId + mentionPublication { + ... on Post { + id + profile { + ...ProfileFields + } + metadata { + content + } + } + ... on Comment { + id + profile { + ...ProfileFields + } + metadata { + content + } + } + } + createdAt + } + ... on NewReactionNotification { + notificationId + profile { + ...ProfileFields + } + publication { + ... on Post { + id + metadata { + content + } + } + ... on Comment { + id + metadata { + content + } + } + ... on Mirror { + id + metadata { + content + } + } + } + createdAt + } + ... on NewCommentNotification { + notificationId + profile { + ...ProfileFields + } + comment { + id + metadata { + content + } + commentOn { + ... on Post { + id + } + ... on Comment { + id + } + ... on Mirror { + id + } + } + } + createdAt + } + ... on NewMirrorNotification { + notificationId + profile { + ...ProfileFields + } + publication { + ... on Post { + id + metadata { + content + } + } + ... on Comment { + id + metadata { + content + } + } + } + createdAt + } + ... on NewCollectNotification { + notificationId + wallet { + address + defaultProfile { + ...ProfileFields + } + } + collectedPublication { + ... on Post { + id + metadata { + content + } + collectModule { + ...CollectModuleFields + } + } + ... on Comment { + id + metadata { + content + } + collectModule { + ...CollectModuleFields + } + } + } + createdAt + } + } + pageInfo { + totalCount + next + } + } +} diff --git a/src/graphql/getProfile.graphql b/src/graphql/queries/getProfile.graphql similarity index 100% rename from src/graphql/getProfile.graphql rename to src/graphql/queries/getProfile.graphql diff --git a/src/graphql/getPublication.graphql b/src/graphql/queries/getPublication.graphql similarity index 100% rename from src/graphql/getPublication.graphql rename to src/graphql/queries/getPublication.graphql diff --git a/src/graphql/getPublications.graphql b/src/graphql/queries/getPublications.graphql similarity index 100% rename from src/graphql/getPublications.graphql rename to src/graphql/queries/getPublications.graphql diff --git a/src/graphql/queries/getReactions.graphql b/src/graphql/queries/getReactions.graphql new file mode 100644 index 0000000..3d8b965 --- /dev/null +++ b/src/graphql/queries/getReactions.graphql @@ -0,0 +1,15 @@ +query reactions($request: WhoReactedPublicationRequest!) { + whoReactedPublication(request: $request) { + items { + reactionId + profile { + ...ProfileFields + isFollowedByMe + } + } + pageInfo { + next + totalCount + } + } +} diff --git a/src/graphql/queries/getRecommendedProfiles.graphql b/src/graphql/queries/getRecommendedProfiles.graphql new file mode 100644 index 0000000..548b1ff --- /dev/null +++ b/src/graphql/queries/getRecommendedProfiles.graphql @@ -0,0 +1,6 @@ +query recommendedProfiles($options: RecommendedProfileOptions) { + recommendedProfiles(options: $options) { + ...ProfileFields + isFollowedByMe + } +} diff --git a/src/graphql/queries/searchProfiles.graphql b/src/graphql/queries/searchProfiles.graphql new file mode 100644 index 0000000..d228ad4 --- /dev/null +++ b/src/graphql/queries/searchProfiles.graphql @@ -0,0 +1,13 @@ +query searchProfiles($request: SearchQueryRequest!) { + search(request: $request) { + ... on ProfileSearchResult { + items { + ...ProfileFields + } + pageInfo { + next + totalCount + } + } + } +} diff --git a/src/graphql/queries/searchPublications.graphql b/src/graphql/queries/searchPublications.graphql new file mode 100644 index 0000000..1a5ce31 --- /dev/null +++ b/src/graphql/queries/searchPublications.graphql @@ -0,0 +1,22 @@ +query searchPublications( + $request: SearchQueryRequest! + $reactionRequest: ReactionFieldResolverRequest + $profileId: ProfileId +) { + search(request: $request) { + ... on PublicationSearchResult { + items { + ... on Post { + ...PostFields + } + ... on Comment { + ...CommentFields + } + } + pageInfo { + next + totalCount + } + } + } +} diff --git a/src/index.ts b/src/index.ts index 6d67f4a..a801971 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,2 +1,2 @@ -export * from './types' -export * from './components' \ No newline at end of file +export * from "./types/types"; +export * from "./components"; diff --git a/src/types.ts b/src/types/types.ts similarity index 58% rename from src/types.ts rename to src/types/types.ts index 4af18d9..9b564e5 100644 --- a/src/types.ts +++ b/src/types/types.ts @@ -7,19 +7,19 @@ import { Comment, Mirror, PaginatedResultInfo, - PublicationMetadataFilters -} from './graphql/generated' + PublicationMetadataFilters, +} from "../graphql/generated"; export type FeedQuery = { name: string; - sortCriteria?: PublicationSortCriteria, - publicationTypes?: PublicationTypes[], + sortCriteria?: PublicationSortCriteria; + publicationTypes?: PublicationTypes[]; limit?: number; profileId?: number; publicationId?: number; cursor?: string; - metadata?: PublicationMetadataFilters -} + metadata?: PublicationMetadataFilters; +}; export type ProfilesQuery = { name: string; @@ -27,17 +27,17 @@ export type ProfilesQuery = { limit?: number; ethereumAddress?: string; cursor?: string; -} +}; export enum Theme { - light = 'light', - dark = 'dark' + light = "light", + dark = "dark", } export enum Environment { - testnet = 'testnet', - mainnet = 'mainnet', - sandbox = 'sandbox', + testnet = "testnet", + mainnet = "mainnet", + sandbox = "sandbox", } export interface LensContextType { @@ -46,17 +46,17 @@ export interface LensContextType { } export enum ThemeColors { - black = '#131313', - white = '#ffffff', - lightGray = 'rgba(255, 255, 255, .6)', - clearWhite = 'rgba(255, 255, 255, .15)' + black = "#131313", + white = "#ffffff", + lightGray = "rgba(255, 255, 255, .6)", + clearWhite = "rgba(255, 255, 255, .15)", } /* Lens specific */ export enum MetadataDisplayType { - number = 'number', - string = 'string', - date = 'date', + number = "number", + string = "string", + date = "date", } export interface SignatureContext { @@ -121,7 +121,10 @@ export interface ExtendedMirror extends Mirror { originalProfile?: ExtendedProfile; } -export type ExtendedPublication = ExtendedComment | ExtendedMirror | ExtendedPost +export type ExtendedPublication = + | ExtendedComment + | ExtendedMirror + | ExtendedPost; export interface PublicationFetchResults { pageInfo: PaginatedResultInfo; @@ -130,58 +133,58 @@ export interface PublicationFetchResults { /* Styles */ export type PublicationStyles = { - publicationWrapper: {}, - publicationContainer: {}, - missingAvatarPlaceholder: {}, - smallAvatar: {}, - postContentContainer: {}, - postText: {}, - metadataImage: {}, - statsContainer: {}, - statsDetailContainer: {}, - statsDetailText: {}, - postOwnerDetailsContainer: {}, - postOwnerName: {}, - postOwnerHandle: {}, - timestamp: {}, - activityIndicatorContainer: {}, - mirrorContainer: {}, - mirrorText: {} -} + publicationWrapper: {}; + publicationContainer: {}; + missingAvatarPlaceholder: {}; + smallAvatar: {}; + postContentContainer: {}; + postText: {}; + metadataImage: {}; + statsContainer: {}; + statsDetailContainer: {}; + statsDetailText: {}; + postOwnerDetailsContainer: {}; + postOwnerName: {}; + postOwnerHandle: {}; + timestamp: {}; + activityIndicatorContainer: {}; + mirrorContainer: {}; + mirrorText: {}; +}; export type ProfileHeaderStyles = { - container: {}, - blankHeader: {}, - headerImage: {}, - avatar: {}, - userDetails: {}, - name: {}, - handle: {}, - bio: {}, - profileStats: {}, - statsData: {}, - statsHeader: {}, - profileFollowingData: {}, - profileFollowerData: {} -} + container: {}; + blankHeader: {}; + headerImage: {}; + avatar: {}; + userDetails: {}; + name: {}; + handle: {}; + bio: {}; + profileStats: {}; + statsData: {}; + statsHeader: {}; + profileFollowingData: {}; + profileFollowerData: {}; +}; export type ProfileListItemStyles = { - container: {}, - avatarContainer: {}, - avatar: {}, - profileName: {}, - profileHandle: {}, - profileBio: {}, - infoContainer: {}, - followButtonContainer: {}, - followButton: {}, - followingButton: {}, - followButtonText: {}, - followingButtonText: {}, -} + container: {}; + avatarContainer: {}; + avatar: {}; + profileName: {}; + profileHandle: {}; + profileBio: {}; + infoContainer: {}; + followButtonContainer: {}; + followButton: {}; + followingButton: {}; + followButtonText: {}; + followingButtonText: {}; +}; export type FeedStyles = { - container: {}, - loadingIndicatorStyle: {}, - noCommentsMessage: {} -} \ No newline at end of file + container: {}; + loadingIndicatorStyle: {}; + noCommentsMessage: {}; +}; diff --git a/src/utils.ts b/src/utils/utils.ts similarity index 100% rename from src/utils.ts rename to src/utils/utils.ts