diff --git a/packages/hypergraph/src/space/find-many-public.ts b/packages/hypergraph/src/space/find-many-public.ts index 62053d47..3714dfa6 100644 --- a/packages/hypergraph/src/space/find-many-public.ts +++ b/packages/hypergraph/src/space/find-many-public.ts @@ -10,7 +10,7 @@ const spaceFields = ` type page { name - relationsList(filter: { + avatarRelations: relationsList(filter: { typeId: { is: "${ContentIds.AVATAR_PROPERTY}"} }) { toEntity { @@ -22,6 +22,18 @@ const spaceFields = ` } } } + coverRelations: relationsList(filter: { + typeId: { is: "${SystemIds.COVER_PROPERTY}"} + }) { + toEntity { + valuesList(filter: { + propertyId: { is: "${SystemIds.IMAGE_URL_PROPERTY}"} + }) { + propertyId + text + } + } + } } editorsList { memberSpaceId @@ -40,26 +52,30 @@ export const PublicSpaceSchema = EffectSchema.Struct({ type: SpaceTypeSchema, name: EffectSchema.String, avatar: EffectSchema.optional(EffectSchema.String), + cover: EffectSchema.optional(EffectSchema.String), editorIds: EffectSchema.Array(EffectSchema.String), memberIds: EffectSchema.Array(EffectSchema.String), }); export type PublicSpace = typeof PublicSpaceSchema.Type; +type RelationEntry = { + toEntity?: { + valuesList?: { + propertyId: string; + text: string | null; + }[]; + } | null; +}; + type SpacesQueryResult = { spaces?: { id: string; type: 'PERSONAL' | 'DAO'; page: { name?: string | null; - relationsList?: { - toEntity?: { - valuesList?: { - propertyId: string; - text: string | null; - }[]; - } | null; - }[]; + avatarRelations?: RelationEntry[]; + coverRelations?: RelationEntry[]; } | null; editorsList?: { memberSpaceId: string; @@ -74,16 +90,24 @@ type SpaceQueryEntry = NonNullable[number]; const decodeSpace = EffectSchema.decodeUnknownEither(PublicSpaceSchema); -const getAvatarFromSpace = (space: SpaceQueryEntry) => { - const firstRelation = space.page?.relationsList?.[0]; +const getImageFromRelations = (relations?: RelationEntry[]) => { + const firstRelation = relations?.[0]; const firstValue = firstRelation?.toEntity?.valuesList?.[0]; - const avatar = firstValue?.text; - if (typeof avatar === 'string') { - return avatar; + const url = firstValue?.text; + if (typeof url === 'string') { + return url; } return undefined; }; +const getAvatarFromSpace = (space: SpaceQueryEntry) => { + return getImageFromRelations(space.page?.avatarRelations); +}; + +const getCoverFromSpace = (space: SpaceQueryEntry) => { + return getImageFromRelations(space.page?.coverRelations); +}; + const getEditorIdsFromSpace = (space: SpaceQueryEntry): string[] => { return (space.editorsList ?? []).map((e) => e.memberSpaceId).filter((id): id is string => typeof id === 'string'); }; @@ -103,6 +127,7 @@ export const parseSpacesQueryResult = (queryResult: SpacesQueryResult) => { type: space.type, name: space.page?.name ?? undefined, avatar: getAvatarFromSpace(space), + cover: getCoverFromSpace(space), editorIds: getEditorIdsFromSpace(space), memberIds: getMemberIdsFromSpace(space), }; diff --git a/packages/hypergraph/test/space/find-many-public.test.ts b/packages/hypergraph/test/space/find-many-public.test.ts index 5d2512f6..5c6ea927 100644 --- a/packages/hypergraph/test/space/find-many-public.test.ts +++ b/packages/hypergraph/test/space/find-many-public.test.ts @@ -1,11 +1,28 @@ import { describe, expect, it } from 'vitest'; import { buildFilterString, buildSpacesQuery, parseSpacesQueryResult } from '../../src/space/find-many-public.js'; +const buildImageRelation = (url: string | null) => [ + { + toEntity: { + valuesList: + url === null + ? [] + : [ + { + propertyId: '8a743832c0944a62b6650c3cc2f9c7bc', + text: url, + }, + ], + }, + }, +]; + const buildQuerySpace = ({ id = 'space-id', type = 'PERSONAL', name = 'Space name', avatar, + cover, editorsList = [], membersList = [], }: { @@ -13,6 +30,7 @@ const buildQuerySpace = ({ type?: 'PERSONAL' | 'DAO'; name?: string | null; avatar?: string | null; + cover?: string | null; editorsList?: { memberSpaceId: string }[]; membersList?: { memberSpaceId: string }[]; } = {}) => { @@ -21,24 +39,8 @@ const buildQuerySpace = ({ type, page: { name, - relationsList: - avatar === undefined - ? [] - : [ - { - toEntity: { - valuesList: - avatar === null - ? [] - : [ - { - propertyId: '8a743832c0944a62b6650c3cc2f9c7bc', - text: avatar, - }, - ], - }, - }, - ], + avatarRelations: avatar === undefined ? [] : buildImageRelation(avatar), + coverRelations: cover === undefined ? [] : buildImageRelation(cover), }, editorsList, membersList, @@ -64,6 +66,39 @@ describe('parseSpacesQueryResult', () => { expect(invalidSpaces).toHaveLength(0); }); + it('parses cover image', () => { + const { data } = parseSpacesQueryResult({ + spaces: [ + buildQuerySpace({ + id: 'space-cover', + name: 'Space with cover', + avatar: 'https://example.com/avatar.png', + cover: 'https://example.com/cover.png', + }), + ], + }); + + expect(data).toEqual([ + { + id: 'space-cover', + type: 'PERSONAL', + name: 'Space with cover', + avatar: 'https://example.com/avatar.png', + cover: 'https://example.com/cover.png', + editorIds: [], + memberIds: [], + }, + ]); + }); + + it('omits cover when not provided', () => { + const { data } = parseSpacesQueryResult({ + spaces: [buildQuerySpace({ id: 'space-no-cover', name: 'No cover' })], + }); + + expect(data[0]?.cover).toBeUndefined(); + }); + it('omits avatar when not provided', () => { const { data } = parseSpacesQueryResult({ spaces: [buildQuerySpace({ id: 'space-2', name: 'Space 2', avatar: undefined })], @@ -243,6 +278,8 @@ describe('buildSpacesQuery', () => { expect(query).toContain('id'); expect(query).toContain('type'); expect(query).toContain('page {'); + expect(query).toContain('avatarRelations:'); + expect(query).toContain('coverRelations:'); expect(query).toContain('editorsList {'); expect(query).toContain('membersList {'); });