|
| 1 | +import { ContentIds, Graph, SystemIds } from '@graphprotocol/grc-20'; |
| 2 | +import * as Either from 'effect/Either'; |
| 3 | +import * as EffectSchema from 'effect/Schema'; |
| 4 | +import { request } from 'graphql-request'; |
| 5 | + |
| 6 | +const spaceFields = ` |
| 7 | + id |
| 8 | + page { |
| 9 | + name |
| 10 | + relationsList(filter: { |
| 11 | + typeId: { is: "${ContentIds.AVATAR_PROPERTY}"} |
| 12 | + }) { |
| 13 | + toEntity { |
| 14 | + valuesList(filter: { |
| 15 | + propertyId: { is: "${SystemIds.IMAGE_URL_PROPERTY}"} |
| 16 | + }) { |
| 17 | + propertyId |
| 18 | + string |
| 19 | + } |
| 20 | + } |
| 21 | + } |
| 22 | + } |
| 23 | +`; |
| 24 | + |
| 25 | +const spacesQueryDocument = ` |
| 26 | +query spaces { |
| 27 | + spaces { |
| 28 | + ${spaceFields} |
| 29 | + } |
| 30 | +} |
| 31 | +`; |
| 32 | + |
| 33 | +const memberSpacesQueryDocument = ` |
| 34 | +query memberSpaces($accountAddress: String!) { |
| 35 | + spaces(filter: {members: {some: {address: {is: $accountAddress}}}}) { |
| 36 | + ${spaceFields} |
| 37 | + } |
| 38 | +} |
| 39 | +`; |
| 40 | + |
| 41 | +const editorSpacesQueryDocument = ` |
| 42 | +query editorSpaces($accountAddress: String!) { |
| 43 | + spaces(filter: {editors: {some: {address: {is: $accountAddress}}}}) { |
| 44 | + ${spaceFields} |
| 45 | + } |
| 46 | +} |
| 47 | +`; |
| 48 | + |
| 49 | +export const PublicSpaceSchema = EffectSchema.Struct({ |
| 50 | + id: EffectSchema.String, |
| 51 | + name: EffectSchema.String, |
| 52 | + avatar: EffectSchema.optional(EffectSchema.String), |
| 53 | +}); |
| 54 | + |
| 55 | +export type PublicSpace = typeof PublicSpaceSchema.Type; |
| 56 | + |
| 57 | +type SpacesQueryResult = { |
| 58 | + spaces?: { |
| 59 | + id: string; |
| 60 | + page: { |
| 61 | + name?: string | null; |
| 62 | + relationsList?: { |
| 63 | + toEntity?: { |
| 64 | + valuesList?: { |
| 65 | + propertyId: string; |
| 66 | + string: string | null; |
| 67 | + }[]; |
| 68 | + } | null; |
| 69 | + }[]; |
| 70 | + } | null; |
| 71 | + }[]; |
| 72 | +}; |
| 73 | + |
| 74 | +type SpacesQueryVariables = { |
| 75 | + accountAddress: string; |
| 76 | +}; |
| 77 | + |
| 78 | +type SpaceQueryEntry = NonNullable<SpacesQueryResult['spaces']>[number]; |
| 79 | + |
| 80 | +const decodeSpace = EffectSchema.decodeUnknownEither(PublicSpaceSchema); |
| 81 | + |
| 82 | +const getAvatarFromSpace = (space: SpaceQueryEntry) => { |
| 83 | + const firstRelation = space.page?.relationsList?.[0]; |
| 84 | + const firstValue = firstRelation?.toEntity?.valuesList?.[0]; |
| 85 | + const avatar = firstValue?.string; |
| 86 | + if (typeof avatar === 'string') { |
| 87 | + return avatar; |
| 88 | + } |
| 89 | + return undefined; |
| 90 | +}; |
| 91 | + |
| 92 | +export const parseSpacesQueryResult = (queryResult: SpacesQueryResult) => { |
| 93 | + const data: PublicSpace[] = []; |
| 94 | + const invalidSpaces: Record<string, unknown>[] = []; |
| 95 | + const spaces = queryResult.spaces ?? []; |
| 96 | + |
| 97 | + for (const space of spaces) { |
| 98 | + const rawSpace: Record<string, unknown> = { |
| 99 | + id: space.id, |
| 100 | + name: space.page?.name ?? undefined, |
| 101 | + avatar: getAvatarFromSpace(space), |
| 102 | + }; |
| 103 | + |
| 104 | + const decodedSpace = decodeSpace(rawSpace); |
| 105 | + |
| 106 | + if (Either.isRight(decodedSpace)) { |
| 107 | + data.push(decodedSpace.right); |
| 108 | + } else { |
| 109 | + invalidSpaces.push(rawSpace); |
| 110 | + } |
| 111 | + } |
| 112 | + |
| 113 | + return { data, invalidSpaces }; |
| 114 | +}; |
| 115 | + |
| 116 | +export type FindManyPublicFilter = |
| 117 | + | Readonly<{ memberAccountAddress: string; editorAccountAddress?: never }> |
| 118 | + | Readonly<{ editorAccountAddress: string; memberAccountAddress?: never }> |
| 119 | + | Readonly<{ memberAccountAddress?: undefined; editorAccountAddress?: undefined }>; |
| 120 | + |
| 121 | +export type FindManyPublicParams = Readonly<{ |
| 122 | + filter?: FindManyPublicFilter; |
| 123 | +}>; |
| 124 | + |
| 125 | +export const findManyPublic = async (params?: FindManyPublicParams) => { |
| 126 | + const filter = params?.filter; |
| 127 | + const memberAccountAddress = filter?.memberAccountAddress; |
| 128 | + const editorAccountAddress = filter?.editorAccountAddress; |
| 129 | + |
| 130 | + if (memberAccountAddress && editorAccountAddress) { |
| 131 | + throw new Error('Provide only one of memberAccountAddress or editorAccountAddress when calling findManyPublic().'); |
| 132 | + } |
| 133 | + |
| 134 | + const endpoint = `${Graph.TESTNET_API_ORIGIN}/graphql`; |
| 135 | + |
| 136 | + if (memberAccountAddress) { |
| 137 | + const queryResult = await request<SpacesQueryResult, SpacesQueryVariables>(endpoint, memberSpacesQueryDocument, { |
| 138 | + accountAddress: memberAccountAddress, |
| 139 | + }); |
| 140 | + return parseSpacesQueryResult(queryResult); |
| 141 | + } |
| 142 | + |
| 143 | + if (editorAccountAddress) { |
| 144 | + const queryResult = await request<SpacesQueryResult, SpacesQueryVariables>(endpoint, editorSpacesQueryDocument, { |
| 145 | + accountAddress: editorAccountAddress, |
| 146 | + }); |
| 147 | + return parseSpacesQueryResult(queryResult); |
| 148 | + } |
| 149 | + |
| 150 | + const queryResult = await request<SpacesQueryResult>(endpoint, spacesQueryDocument); |
| 151 | + return parseSpacesQueryResult(queryResult); |
| 152 | +}; |
0 commit comments