diff --git a/packages/components/CHANGELOG.md b/packages/components/CHANGELOG.md index 3ba8cf60..1228d5ef 100644 --- a/packages/components/CHANGELOG.md +++ b/packages/components/CHANGELOG.md @@ -1,5 +1,15 @@ # @baseapp-frontend/components +## 0.0.38 + +### Patch Changes + +- Add the ability to create new chat groups. +- Fix a few height bugs on Virtuoso lists. + +- Updated dependencies + - @baseapp-frontend/design-system@0.0.26 + ## 0.0.37 ### Patch Changes diff --git a/packages/components/__generated__/ChatRoomHeaderFragment.graphql.ts b/packages/components/__generated__/ChatRoomHeaderFragment.graphql.ts index 31baf7d5..3ce271c2 100644 --- a/packages/components/__generated__/ChatRoomHeaderFragment.graphql.ts +++ b/packages/components/__generated__/ChatRoomHeaderFragment.graphql.ts @@ -1,5 +1,5 @@ /** - * @generated SignedSource<<5e808251434895d9c0aa7fb0d0631353>> + * @generated SignedSource<> * @lightSyntaxTransform * @nogrep */ @@ -18,6 +18,7 @@ export type ChatRoomHeaderFragment$data = { } | null | undefined + readonly isGroup: boolean readonly participants: | { readonly edges: ReadonlyArray< @@ -99,6 +100,13 @@ const node: ReaderFragment = (function () { name: 'title', storageKey: null, }, + { + alias: null, + args: null, + kind: 'ScalarField', + name: 'isGroup', + storageKey: null, + }, { alias: null, args: null, @@ -164,6 +172,6 @@ const node: ReaderFragment = (function () { } })() -;(node as any).hash = '7983eaa530f167bdd9363a4aa2eade2d' +;(node as any).hash = 'f2a6eba3e9bc5d8ca187b1be954d3278' export default node diff --git a/packages/components/__generated__/ChatRoomMessagesListPaginationQuery.graphql.ts b/packages/components/__generated__/ChatRoomMessagesListPaginationQuery.graphql.ts index 142b8740..83d62737 100644 --- a/packages/components/__generated__/ChatRoomMessagesListPaginationQuery.graphql.ts +++ b/packages/components/__generated__/ChatRoomMessagesListPaginationQuery.graphql.ts @@ -1,5 +1,5 @@ /** - * @generated SignedSource<> + * @generated SignedSource<> * @lightSyntaxTransform * @nogrep */ @@ -157,7 +157,14 @@ const node: ConcreteRequest = (function () { { alias: null, args: null, - concreteType: 'UnreadMessages', + kind: 'ScalarField', + name: 'isGroup', + storageKey: null, + }, + { + alias: null, + args: null, + concreteType: 'UnreadMessageCount', kind: 'LinkedField', name: 'unreadMessages', plural: false, @@ -176,6 +183,7 @@ const node: ConcreteRequest = (function () { name: 'markedUnread', storageKey: null, }, + v3 /*: any*/, ], storageKey: null, }, @@ -366,16 +374,16 @@ const node: ConcreteRequest = (function () { ], }, params: { - cacheID: 'bfd8d21c03b208e2901e4fe25aeb55ac', + cacheID: '61e5e98a44d6c67b835e618645d03db0', id: null, metadata: {}, name: 'ChatRoomMessagesListPaginationQuery', operationKind: 'query', - text: 'query ChatRoomMessagesListPaginationQuery(\n $count: Int = 20\n $cursor: String\n $id: ID!\n) {\n node(id: $id) {\n __typename\n ...MessagesListFragment_1G22uz\n id\n }\n}\n\nfragment MessageItemFragment on Message {\n id\n content\n created\n extraData\n inReplyTo {\n id\n }\n isRead\n pk\n profile {\n id\n }\n verb\n}\n\nfragment MessagesListFragment_1G22uz on ChatRoom {\n id\n participants {\n totalCount\n }\n unreadMessages {\n count\n markedUnread\n }\n allMessages(first: $count, after: $cursor) {\n totalCount\n edges {\n node {\n id\n created\n profile {\n id\n name\n image(height: 32, width: 32) {\n url\n }\n }\n isRead\n ...MessageItemFragment\n __typename\n }\n cursor\n }\n pageInfo {\n hasNextPage\n endCursor\n }\n }\n}\n', + text: 'query ChatRoomMessagesListPaginationQuery(\n $count: Int = 20\n $cursor: String\n $id: ID!\n) {\n node(id: $id) {\n __typename\n ...MessagesListFragment_1G22uz\n id\n }\n}\n\nfragment MessageItemFragment on Message {\n id\n content\n created\n extraData\n inReplyTo {\n id\n }\n isRead\n pk\n profile {\n id\n }\n verb\n}\n\nfragment MessagesListFragment_1G22uz on ChatRoom {\n id\n participants {\n totalCount\n }\n isGroup\n unreadMessages {\n count\n markedUnread\n id\n }\n allMessages(first: $count, after: $cursor) {\n totalCount\n edges {\n node {\n id\n created\n profile {\n id\n name\n image(height: 32, width: 32) {\n url\n }\n }\n isRead\n ...MessageItemFragment\n __typename\n }\n cursor\n }\n pageInfo {\n hasNextPage\n endCursor\n }\n }\n}\n', }, } })() -;(node as any).hash = '8137c492c793a174c334ca5178c5ce22' +;(node as any).hash = 'b2b4de64aa6db0164832a66bf93d8992' export default node diff --git a/packages/components/__generated__/ChatRoomQuery.graphql.ts b/packages/components/__generated__/ChatRoomQuery.graphql.ts index b0af60b3..a03dfedf 100644 --- a/packages/components/__generated__/ChatRoomQuery.graphql.ts +++ b/packages/components/__generated__/ChatRoomQuery.graphql.ts @@ -1,5 +1,5 @@ /** - * @generated SignedSource<> + * @generated SignedSource<> * @lightSyntaxTransform * @nogrep */ @@ -159,6 +159,13 @@ const node: ConcreteRequest = (function () { name: 'title', storageKey: null, }, + { + alias: null, + args: null, + kind: 'ScalarField', + name: 'isGroup', + storageKey: null, + }, { alias: null, args: null, @@ -207,7 +214,7 @@ const node: ConcreteRequest = (function () { { alias: null, args: null, - concreteType: 'UnreadMessages', + concreteType: 'UnreadMessageCount', kind: 'LinkedField', name: 'unreadMessages', plural: false, @@ -226,6 +233,7 @@ const node: ConcreteRequest = (function () { name: 'markedUnread', storageKey: null, }, + v2 /*: any*/, ], storageKey: null, }, @@ -404,12 +412,12 @@ const node: ConcreteRequest = (function () { ], }, params: { - cacheID: '4f5e1119794e184995aef3553f6e5fc0', + cacheID: '27e915f056eced4ea436c1123aeaebf3', id: null, metadata: {}, name: 'ChatRoomQuery', operationKind: 'query', - text: 'query ChatRoomQuery(\n $roomId: ID!\n) {\n chatRoom(id: $roomId) {\n id\n ...ChatRoomHeaderFragment\n ...MessagesListFragment\n }\n}\n\nfragment ChatRoomHeaderFragment on ChatRoom {\n image(width: 100, height: 100) {\n url\n }\n title\n participants {\n edges {\n node {\n profile {\n id\n name\n image(width: 100, height: 100) {\n url\n }\n }\n id\n }\n }\n }\n}\n\nfragment MessageItemFragment on Message {\n id\n content\n created\n extraData\n inReplyTo {\n id\n }\n isRead\n pk\n profile {\n id\n }\n verb\n}\n\nfragment MessagesListFragment on ChatRoom {\n id\n participants {\n totalCount\n }\n unreadMessages {\n count\n markedUnread\n }\n allMessages(first: 20) {\n totalCount\n edges {\n node {\n id\n created\n profile {\n id\n name\n image(height: 32, width: 32) {\n url\n }\n }\n isRead\n ...MessageItemFragment\n __typename\n }\n cursor\n }\n pageInfo {\n hasNextPage\n endCursor\n }\n }\n}\n', + text: 'query ChatRoomQuery(\n $roomId: ID!\n) {\n chatRoom(id: $roomId) {\n id\n ...ChatRoomHeaderFragment\n ...MessagesListFragment\n }\n}\n\nfragment ChatRoomHeaderFragment on ChatRoom {\n image(width: 100, height: 100) {\n url\n }\n title\n isGroup\n participants {\n edges {\n node {\n profile {\n id\n name\n image(width: 100, height: 100) {\n url\n }\n }\n id\n }\n }\n }\n}\n\nfragment MessageItemFragment on Message {\n id\n content\n created\n extraData\n inReplyTo {\n id\n }\n isRead\n pk\n profile {\n id\n }\n verb\n}\n\nfragment MessagesListFragment on ChatRoom {\n id\n participants {\n totalCount\n }\n isGroup\n unreadMessages {\n count\n markedUnread\n id\n }\n allMessages(first: 20) {\n totalCount\n edges {\n node {\n id\n created\n profile {\n id\n name\n image(height: 32, width: 32) {\n url\n }\n }\n isRead\n ...MessageItemFragment\n __typename\n }\n cursor\n }\n pageInfo {\n hasNextPage\n endCursor\n }\n }\n}\n', }, } })() diff --git a/packages/components/__generated__/ChatRoomsQuery.graphql.ts b/packages/components/__generated__/ChatRoomsQuery.graphql.ts index 80bbcb76..36f049c8 100644 --- a/packages/components/__generated__/ChatRoomsQuery.graphql.ts +++ b/packages/components/__generated__/ChatRoomsQuery.graphql.ts @@ -1,5 +1,5 @@ /** - * @generated SignedSource<<1b323607818160897b0a6ff121fc78ca>> + * @generated SignedSource<<8de8c94cae930c0c433d32612e785daa>> * @lightSyntaxTransform * @nogrep */ @@ -332,7 +332,7 @@ const node: ConcreteRequest = (function () { { alias: null, args: null, - concreteType: 'UnreadMessages', + concreteType: 'UnreadMessageCount', kind: 'LinkedField', name: 'unreadMessages', plural: false, @@ -351,6 +351,7 @@ const node: ConcreteRequest = (function () { name: 'markedUnread', storageKey: null, }, + v0 /*: any*/, ], storageKey: null, }, @@ -379,6 +380,13 @@ const node: ConcreteRequest = (function () { name: 'title', storageKey: null, }, + { + alias: null, + args: null, + kind: 'ScalarField', + name: 'isGroup', + storageKey: null, + }, { alias: null, args: null, @@ -589,12 +597,12 @@ const node: ConcreteRequest = (function () { ], }, params: { - cacheID: '7bbfb495d5690e7598b6716da8589bea', + cacheID: 'e1671d0045de33fad71d51afa2bc42d4', id: null, metadata: {}, name: 'ChatRoomsQuery', operationKind: 'query', - text: 'query ChatRoomsQuery {\n ...AllProfilesListFragment\n me {\n id\n profile {\n id\n ...RoomsListFragment\n }\n }\n}\n\nfragment AllProfilesListFragment on Query {\n allProfiles(first: 5, orderBy: "-created") {\n totalCount\n pageInfo {\n hasNextPage\n endCursor\n }\n edges {\n node {\n id\n ...ProfileItemFragment\n __typename\n }\n cursor\n }\n }\n}\n\nfragment ChatRoomHeaderFragment on ChatRoom {\n image(width: 100, height: 100) {\n url\n }\n title\n participants {\n edges {\n node {\n profile {\n id\n name\n image(width: 100, height: 100) {\n url\n }\n }\n id\n }\n }\n }\n}\n\nfragment MessageItemFragment on Message {\n id\n content\n created\n extraData\n inReplyTo {\n id\n }\n isRead\n pk\n profile {\n id\n }\n verb\n}\n\nfragment MessagesListFragment on ChatRoom {\n id\n participants {\n totalCount\n }\n unreadMessages {\n count\n markedUnread\n }\n allMessages(first: 20) {\n totalCount\n edges {\n node {\n id\n created\n profile {\n id\n name\n image(height: 32, width: 32) {\n url\n }\n }\n isRead\n ...MessageItemFragment\n __typename\n }\n cursor\n }\n pageInfo {\n hasNextPage\n endCursor\n }\n }\n}\n\nfragment ProfileItemFragment on Profile {\n id\n name\n image(width: 100, height: 100) {\n url\n }\n urlPath {\n path\n id\n }\n}\n\nfragment RoomFragment on ChatRoom {\n id\n unreadMessages {\n count\n markedUnread\n }\n lastMessageTime\n lastMessage {\n id\n content\n }\n ...ChatRoomHeaderFragment\n ...MessagesListFragment\n}\n\nfragment RoomsListFragment on ChatRoomsInterface {\n __isChatRoomsInterface: __typename\n chatRooms(first: 5, unreadMessages: false, archived: false) {\n edges {\n node {\n id\n ...RoomFragment\n __typename\n }\n cursor\n }\n pageInfo {\n hasNextPage\n endCursor\n }\n }\n id\n}\n', + text: 'query ChatRoomsQuery {\n ...AllProfilesListFragment\n me {\n id\n profile {\n id\n ...RoomsListFragment\n }\n }\n}\n\nfragment AllProfilesListFragment on Query {\n allProfiles(first: 5, orderBy: "-created") {\n totalCount\n pageInfo {\n hasNextPage\n endCursor\n }\n edges {\n node {\n id\n ...ProfileItemFragment\n __typename\n }\n cursor\n }\n }\n}\n\nfragment ChatRoomHeaderFragment on ChatRoom {\n image(width: 100, height: 100) {\n url\n }\n title\n isGroup\n participants {\n edges {\n node {\n profile {\n id\n name\n image(width: 100, height: 100) {\n url\n }\n }\n id\n }\n }\n }\n}\n\nfragment MessageItemFragment on Message {\n id\n content\n created\n extraData\n inReplyTo {\n id\n }\n isRead\n pk\n profile {\n id\n }\n verb\n}\n\nfragment MessagesListFragment on ChatRoom {\n id\n participants {\n totalCount\n }\n isGroup\n unreadMessages {\n count\n markedUnread\n id\n }\n allMessages(first: 20) {\n totalCount\n edges {\n node {\n id\n created\n profile {\n id\n name\n image(height: 32, width: 32) {\n url\n }\n }\n isRead\n ...MessageItemFragment\n __typename\n }\n cursor\n }\n pageInfo {\n hasNextPage\n endCursor\n }\n }\n}\n\nfragment ProfileItemFragment on Profile {\n id\n name\n image(width: 100, height: 100) {\n url\n }\n urlPath {\n path\n id\n }\n}\n\nfragment RoomFragment on ChatRoom {\n id\n unreadMessages {\n count\n markedUnread\n id\n }\n lastMessageTime\n lastMessage {\n id\n content\n }\n ...ChatRoomHeaderFragment\n ...MessagesListFragment\n}\n\nfragment RoomsListFragment on ChatRoomsInterface {\n __isChatRoomsInterface: __typename\n chatRooms(first: 5, unreadMessages: false, archived: false) {\n edges {\n node {\n id\n ...RoomFragment\n __typename\n }\n cursor\n }\n pageInfo {\n hasNextPage\n endCursor\n }\n }\n id\n}\n', }, } })() diff --git a/packages/components/__generated__/CreateChatRoomMutation.graphql.ts b/packages/components/__generated__/CreateChatRoomMutation.graphql.ts index ca7154fd..40e09965 100644 --- a/packages/components/__generated__/CreateChatRoomMutation.graphql.ts +++ b/packages/components/__generated__/CreateChatRoomMutation.graphql.ts @@ -1,5 +1,5 @@ /** - * @generated SignedSource<<43514a1c6cbb856a35e531c83522581e>> + * @generated SignedSource<<3f41c0c4ebcf9dd0b095ff92aecb0a61>> * @lightSyntaxTransform * @nogrep */ @@ -13,8 +13,10 @@ import { FragmentRefs } from 'relay-runtime' export type ChatRoomCreateInput = { clientMutationId?: string | null | undefined + isGroup?: boolean | null | undefined participants: ReadonlyArray profileId: string + title?: string | null | undefined } export type CreateChatRoomMutation$variables = { connections: ReadonlyArray @@ -39,6 +41,13 @@ export type CreateChatRoomMutation$data = { readonly node: | { readonly id: string + readonly image: + | { + readonly url: string + } + | null + | undefined + readonly isGroup: boolean readonly participants: | { readonly edges: ReadonlyArray< @@ -56,6 +65,7 @@ export type CreateChatRoomMutation$data = { } | null | undefined + readonly title: string | null | undefined readonly ' $fragmentSpreads': FragmentRefs<'RoomFragment'> } | null @@ -97,40 +107,21 @@ const node: ConcreteRequest = (function () { name: 'id', storageKey: null, }, - v4 = [v3 /*: any*/], - v5 = { + v4 = { alias: null, args: null, - concreteType: 'ErrorType', - kind: 'LinkedField', - name: 'errors', - plural: true, - selections: [ - { - alias: null, - args: null, - kind: 'ScalarField', - name: 'field', - storageKey: null, - }, - { - alias: null, - args: null, - kind: 'ScalarField', - name: 'messages', - storageKey: null, - }, - ], + kind: 'ScalarField', + name: 'isGroup', storageKey: null, }, - v6 = { + v5 = { alias: null, args: null, kind: 'ScalarField', - name: 'name', + name: 'title', storageKey: null, }, - v7 = [ + v6 = [ { alias: null, args: null, @@ -139,7 +130,7 @@ const node: ConcreteRequest = (function () { storageKey: null, }, ], - v8 = { + v7 = { alias: null, args: [ { @@ -157,24 +148,57 @@ const node: ConcreteRequest = (function () { kind: 'LinkedField', name: 'image', plural: false, - selections: v7 /*: any*/, + selections: v6 /*: any*/, storageKey: 'image(height:100,width:100)', }, + v8 = [v3 /*: any*/], v9 = { + alias: null, + args: null, + concreteType: 'ErrorType', + kind: 'LinkedField', + name: 'errors', + plural: true, + selections: [ + { + alias: null, + args: null, + kind: 'ScalarField', + name: 'field', + storageKey: null, + }, + { + alias: null, + args: null, + kind: 'ScalarField', + name: 'messages', + storageKey: null, + }, + ], + storageKey: null, + }, + v10 = { + alias: null, + args: null, + kind: 'ScalarField', + name: 'name', + storageKey: null, + }, + v11 = { alias: null, args: null, kind: 'ScalarField', name: 'totalCount', storageKey: null, }, - v10 = { + v12 = { alias: null, args: null, kind: 'ScalarField', name: 'content', storageKey: null, }, - v11 = [ + v13 = [ { kind: 'Literal', name: 'first', @@ -213,6 +237,9 @@ const node: ConcreteRequest = (function () { plural: false, selections: [ v3 /*: any*/, + v4 /*: any*/, + v5 /*: any*/, + v7 /*: any*/, { alias: null, args: null, @@ -236,7 +263,7 @@ const node: ConcreteRequest = (function () { kind: 'LinkedField', name: 'node', plural: false, - selections: v4 /*: any*/, + selections: v8 /*: any*/, storageKey: null, }, ], @@ -256,7 +283,7 @@ const node: ConcreteRequest = (function () { ], storageKey: null, }, - v5 /*: any*/, + v9 /*: any*/, ], storageKey: null, }, @@ -295,6 +322,9 @@ const node: ConcreteRequest = (function () { plural: false, selections: [ v3 /*: any*/, + v4 /*: any*/, + v5 /*: any*/, + v7 /*: any*/, { alias: null, args: null, @@ -327,7 +357,7 @@ const node: ConcreteRequest = (function () { kind: 'LinkedField', name: 'profile', plural: false, - selections: [v3 /*: any*/, v6 /*: any*/, v8 /*: any*/], + selections: [v3 /*: any*/, v10 /*: any*/, v7 /*: any*/], storageKey: null, }, ], @@ -336,14 +366,14 @@ const node: ConcreteRequest = (function () { ], storageKey: null, }, - v9 /*: any*/, + v11 /*: any*/, ], storageKey: null, }, { alias: null, args: null, - concreteType: 'UnreadMessages', + concreteType: 'UnreadMessageCount', kind: 'LinkedField', name: 'unreadMessages', plural: false, @@ -362,6 +392,7 @@ const node: ConcreteRequest = (function () { name: 'markedUnread', storageKey: null, }, + v3 /*: any*/, ], storageKey: null, }, @@ -379,26 +410,18 @@ const node: ConcreteRequest = (function () { kind: 'LinkedField', name: 'lastMessage', plural: false, - selections: [v3 /*: any*/, v10 /*: any*/], - storageKey: null, - }, - v8 /*: any*/, - { - alias: null, - args: null, - kind: 'ScalarField', - name: 'title', + selections: [v3 /*: any*/, v12 /*: any*/], storageKey: null, }, { alias: null, - args: v11 /*: any*/, + args: v13 /*: any*/, concreteType: 'MessageConnection', kind: 'LinkedField', name: 'allMessages', plural: false, selections: [ - v9 /*: any*/, + v11 /*: any*/, { alias: null, args: null, @@ -432,7 +455,7 @@ const node: ConcreteRequest = (function () { plural: false, selections: [ v3 /*: any*/, - v6 /*: any*/, + v10 /*: any*/, { alias: null, args: [ @@ -451,7 +474,7 @@ const node: ConcreteRequest = (function () { kind: 'LinkedField', name: 'image', plural: false, - selections: v7 /*: any*/, + selections: v6 /*: any*/, storageKey: 'image(height:32,width:32)', }, ], @@ -464,7 +487,7 @@ const node: ConcreteRequest = (function () { name: 'isRead', storageKey: null, }, - v10 /*: any*/, + v12 /*: any*/, { alias: null, args: null, @@ -479,7 +502,7 @@ const node: ConcreteRequest = (function () { kind: 'LinkedField', name: 'inReplyTo', plural: false, - selections: v4 /*: any*/, + selections: v8 /*: any*/, storageKey: null, }, { @@ -546,7 +569,7 @@ const node: ConcreteRequest = (function () { }, { alias: null, - args: v11 /*: any*/, + args: v13 /*: any*/, filters: null, handle: 'connection', key: 'chatRoom_allMessages', @@ -575,23 +598,23 @@ const node: ConcreteRequest = (function () { }, ], }, - v5 /*: any*/, + v9 /*: any*/, ], storageKey: null, }, ], }, params: { - cacheID: '78d61980ce3fed08c783745e4ffa7587', + cacheID: 'a8e06d2d310d7e4e7d1099dd7284caba', id: null, metadata: {}, name: 'CreateChatRoomMutation', operationKind: 'mutation', - text: 'mutation CreateChatRoomMutation(\n $input: ChatRoomCreateInput!\n) {\n chatRoomCreate(input: $input) {\n room {\n node {\n id\n participants {\n edges {\n node {\n id\n }\n }\n }\n ...RoomFragment\n }\n }\n errors {\n field\n messages\n }\n }\n}\n\nfragment ChatRoomHeaderFragment on ChatRoom {\n image(width: 100, height: 100) {\n url\n }\n title\n participants {\n edges {\n node {\n profile {\n id\n name\n image(width: 100, height: 100) {\n url\n }\n }\n id\n }\n }\n }\n}\n\nfragment MessageItemFragment on Message {\n id\n content\n created\n extraData\n inReplyTo {\n id\n }\n isRead\n pk\n profile {\n id\n }\n verb\n}\n\nfragment MessagesListFragment on ChatRoom {\n id\n participants {\n totalCount\n }\n unreadMessages {\n count\n markedUnread\n }\n allMessages(first: 20) {\n totalCount\n edges {\n node {\n id\n created\n profile {\n id\n name\n image(height: 32, width: 32) {\n url\n }\n }\n isRead\n ...MessageItemFragment\n __typename\n }\n cursor\n }\n pageInfo {\n hasNextPage\n endCursor\n }\n }\n}\n\nfragment RoomFragment on ChatRoom {\n id\n unreadMessages {\n count\n markedUnread\n }\n lastMessageTime\n lastMessage {\n id\n content\n }\n ...ChatRoomHeaderFragment\n ...MessagesListFragment\n}\n', + text: 'mutation CreateChatRoomMutation(\n $input: ChatRoomCreateInput!\n) {\n chatRoomCreate(input: $input) {\n room {\n node {\n id\n isGroup\n title\n image(width: 100, height: 100) {\n url\n }\n participants {\n edges {\n node {\n id\n }\n }\n }\n ...RoomFragment\n }\n }\n errors {\n field\n messages\n }\n }\n}\n\nfragment ChatRoomHeaderFragment on ChatRoom {\n image(width: 100, height: 100) {\n url\n }\n title\n isGroup\n participants {\n edges {\n node {\n profile {\n id\n name\n image(width: 100, height: 100) {\n url\n }\n }\n id\n }\n }\n }\n}\n\nfragment MessageItemFragment on Message {\n id\n content\n created\n extraData\n inReplyTo {\n id\n }\n isRead\n pk\n profile {\n id\n }\n verb\n}\n\nfragment MessagesListFragment on ChatRoom {\n id\n participants {\n totalCount\n }\n isGroup\n unreadMessages {\n count\n markedUnread\n id\n }\n allMessages(first: 20) {\n totalCount\n edges {\n node {\n id\n created\n profile {\n id\n name\n image(height: 32, width: 32) {\n url\n }\n }\n isRead\n ...MessageItemFragment\n __typename\n }\n cursor\n }\n pageInfo {\n hasNextPage\n endCursor\n }\n }\n}\n\nfragment RoomFragment on ChatRoom {\n id\n unreadMessages {\n count\n markedUnread\n id\n }\n lastMessageTime\n lastMessage {\n id\n content\n }\n ...ChatRoomHeaderFragment\n ...MessagesListFragment\n}\n', }, } })() -;(node as any).hash = '0c8de3803eb96cb2bd64dd751320a8a0' +;(node as any).hash = 'b857d9b6ca9707fee4442818703fa259' export default node diff --git a/packages/components/__generated__/MessagesListFragment.graphql.ts b/packages/components/__generated__/MessagesListFragment.graphql.ts index 8f68e1b6..5f51b582 100644 --- a/packages/components/__generated__/MessagesListFragment.graphql.ts +++ b/packages/components/__generated__/MessagesListFragment.graphql.ts @@ -1,5 +1,5 @@ /** - * @generated SignedSource<<8340b33c4779ab7d8426e74e1ec2236f>> + * @generated SignedSource<<4f19333933804219c017733361b7245d>> * @lightSyntaxTransform * @nogrep */ @@ -51,6 +51,7 @@ export type MessagesListFragment$data = { | null | undefined readonly id: string + readonly isGroup: boolean readonly participants: | { readonly totalCount: number | null | undefined @@ -59,8 +60,8 @@ export type MessagesListFragment$data = { | undefined readonly unreadMessages: | { - readonly count: number | null | undefined - readonly markedUnread: boolean | null | undefined + readonly count: number + readonly markedUnread: boolean } | null | undefined @@ -143,7 +144,14 @@ const node: ReaderFragment = (function () { { alias: null, args: null, - concreteType: 'UnreadMessages', + kind: 'ScalarField', + name: 'isGroup', + storageKey: null, + }, + { + alias: null, + args: null, + concreteType: 'UnreadMessageCount', kind: 'LinkedField', name: 'unreadMessages', plural: false, @@ -312,6 +320,6 @@ const node: ReaderFragment = (function () { } })() -;(node as any).hash = '8137c492c793a174c334ca5178c5ce22' +;(node as any).hash = 'b2b4de64aa6db0164832a66bf93d8992' export default node diff --git a/packages/components/__generated__/ReadMessagesMutation.graphql.ts b/packages/components/__generated__/ReadMessagesMutation.graphql.ts index b04dc117..167b32c4 100644 --- a/packages/components/__generated__/ReadMessagesMutation.graphql.ts +++ b/packages/components/__generated__/ReadMessagesMutation.graphql.ts @@ -1,5 +1,5 @@ /** - * @generated SignedSource<<2f9b0ec06977248c81c9044cc03ab7e3>> + * @generated SignedSource<<33c826c589d1c31bf9dcd78890c3fd35>> * @lightSyntaxTransform * @nogrep */ @@ -39,8 +39,8 @@ export type ReadMessagesMutation$data = { readonly id: string readonly unreadMessages: | { - readonly count: number | null | undefined - readonly markedUnread: boolean | null | undefined + readonly count: number + readonly markedUnread: boolean } | null | undefined @@ -82,29 +82,18 @@ const node: ConcreteRequest = (function () { v3 = { alias: null, args: null, - concreteType: 'UnreadMessages', - kind: 'LinkedField', - name: 'unreadMessages', - plural: false, - selections: [ - { - alias: null, - args: null, - kind: 'ScalarField', - name: 'count', - storageKey: null, - }, - { - alias: null, - args: null, - kind: 'ScalarField', - name: 'markedUnread', - storageKey: null, - }, - ], + kind: 'ScalarField', + name: 'count', storageKey: null, }, v4 = { + alias: null, + args: null, + kind: 'ScalarField', + name: 'markedUnread', + storageKey: null, + }, + v5 = { alias: null, args: null, concreteType: 'ErrorType', @@ -129,14 +118,14 @@ const node: ConcreteRequest = (function () { ], storageKey: null, }, - v5 = { + v6 = { alias: null, args: null, kind: 'ScalarField', name: 'content', storageKey: null, }, - v6 = [ + v7 = [ { alias: null, args: null, @@ -145,7 +134,7 @@ const node: ConcreteRequest = (function () { storageKey: null, }, ], - v7 = { + v8 = { alias: null, args: [ { @@ -163,24 +152,24 @@ const node: ConcreteRequest = (function () { kind: 'LinkedField', name: 'image', plural: false, - selections: v6 /*: any*/, + selections: v7 /*: any*/, storageKey: 'image(height:100,width:100)', }, - v8 = { + v9 = { alias: null, args: null, kind: 'ScalarField', name: 'name', storageKey: null, }, - v9 = { + v10 = { alias: null, args: null, kind: 'ScalarField', name: 'totalCount', storageKey: null, }, - v10 = [ + v11 = [ { kind: 'Literal', name: 'first', @@ -211,7 +200,16 @@ const node: ConcreteRequest = (function () { plural: false, selections: [ v2 /*: any*/, - v3 /*: any*/, + { + alias: null, + args: null, + concreteType: 'UnreadMessageCount', + kind: 'LinkedField', + name: 'unreadMessages', + plural: false, + selections: [v3 /*: any*/, v4 /*: any*/], + storageKey: null, + }, { args: null, kind: 'FragmentSpread', @@ -220,7 +218,7 @@ const node: ConcreteRequest = (function () { ], storageKey: null, }, - v4 /*: any*/, + v5 /*: any*/, ], storageKey: null, }, @@ -251,7 +249,16 @@ const node: ConcreteRequest = (function () { plural: false, selections: [ v2 /*: any*/, - v3 /*: any*/, + { + alias: null, + args: null, + concreteType: 'UnreadMessageCount', + kind: 'LinkedField', + name: 'unreadMessages', + plural: false, + selections: [v3 /*: any*/, v4 /*: any*/, v2 /*: any*/], + storageKey: null, + }, { alias: null, args: null, @@ -266,10 +273,10 @@ const node: ConcreteRequest = (function () { kind: 'LinkedField', name: 'lastMessage', plural: false, - selections: [v2 /*: any*/, v5 /*: any*/], + selections: [v2 /*: any*/, v6 /*: any*/], storageKey: null, }, - v7 /*: any*/, + v8 /*: any*/, { alias: null, args: null, @@ -277,6 +284,13 @@ const node: ConcreteRequest = (function () { name: 'title', storageKey: null, }, + { + alias: null, + args: null, + kind: 'ScalarField', + name: 'isGroup', + storageKey: null, + }, { alias: null, args: null, @@ -308,7 +322,7 @@ const node: ConcreteRequest = (function () { kind: 'LinkedField', name: 'profile', plural: false, - selections: [v2 /*: any*/, v8 /*: any*/, v7 /*: any*/], + selections: [v2 /*: any*/, v9 /*: any*/, v8 /*: any*/], storageKey: null, }, v2 /*: any*/, @@ -318,19 +332,19 @@ const node: ConcreteRequest = (function () { ], storageKey: null, }, - v9 /*: any*/, + v10 /*: any*/, ], storageKey: null, }, { alias: null, - args: v10 /*: any*/, + args: v11 /*: any*/, concreteType: 'MessageConnection', kind: 'LinkedField', name: 'allMessages', plural: false, selections: [ - v9 /*: any*/, + v10 /*: any*/, { alias: null, args: null, @@ -364,7 +378,7 @@ const node: ConcreteRequest = (function () { plural: false, selections: [ v2 /*: any*/, - v8 /*: any*/, + v9 /*: any*/, { alias: null, args: [ @@ -383,7 +397,7 @@ const node: ConcreteRequest = (function () { kind: 'LinkedField', name: 'image', plural: false, - selections: v6 /*: any*/, + selections: v7 /*: any*/, storageKey: 'image(height:32,width:32)', }, ], @@ -396,7 +410,7 @@ const node: ConcreteRequest = (function () { name: 'isRead', storageKey: null, }, - v5 /*: any*/, + v6 /*: any*/, { alias: null, args: null, @@ -478,7 +492,7 @@ const node: ConcreteRequest = (function () { }, { alias: null, - args: v10 /*: any*/, + args: v11 /*: any*/, filters: null, handle: 'connection', key: 'chatRoom_allMessages', @@ -488,19 +502,19 @@ const node: ConcreteRequest = (function () { ], storageKey: null, }, - v4 /*: any*/, + v5 /*: any*/, ], storageKey: null, }, ], }, params: { - cacheID: '16aa5685f87fd675f7dd450bdc29fd04', + cacheID: '40c6d59bd74956ca61c5dc1297e0c148', id: null, metadata: {}, name: 'ReadMessagesMutation', operationKind: 'mutation', - text: 'mutation ReadMessagesMutation(\n $input: ChatRoomReadMessagesInput!\n) {\n chatRoomReadMessages(input: $input) {\n room {\n id\n unreadMessages {\n count\n markedUnread\n }\n ...RoomFragment\n }\n errors {\n field\n messages\n }\n }\n}\n\nfragment ChatRoomHeaderFragment on ChatRoom {\n image(width: 100, height: 100) {\n url\n }\n title\n participants {\n edges {\n node {\n profile {\n id\n name\n image(width: 100, height: 100) {\n url\n }\n }\n id\n }\n }\n }\n}\n\nfragment MessageItemFragment on Message {\n id\n content\n created\n extraData\n inReplyTo {\n id\n }\n isRead\n pk\n profile {\n id\n }\n verb\n}\n\nfragment MessagesListFragment on ChatRoom {\n id\n participants {\n totalCount\n }\n unreadMessages {\n count\n markedUnread\n }\n allMessages(first: 20) {\n totalCount\n edges {\n node {\n id\n created\n profile {\n id\n name\n image(height: 32, width: 32) {\n url\n }\n }\n isRead\n ...MessageItemFragment\n __typename\n }\n cursor\n }\n pageInfo {\n hasNextPage\n endCursor\n }\n }\n}\n\nfragment RoomFragment on ChatRoom {\n id\n unreadMessages {\n count\n markedUnread\n }\n lastMessageTime\n lastMessage {\n id\n content\n }\n ...ChatRoomHeaderFragment\n ...MessagesListFragment\n}\n', + text: 'mutation ReadMessagesMutation(\n $input: ChatRoomReadMessagesInput!\n) {\n chatRoomReadMessages(input: $input) {\n room {\n id\n unreadMessages {\n count\n markedUnread\n id\n }\n ...RoomFragment\n }\n errors {\n field\n messages\n }\n }\n}\n\nfragment ChatRoomHeaderFragment on ChatRoom {\n image(width: 100, height: 100) {\n url\n }\n title\n isGroup\n participants {\n edges {\n node {\n profile {\n id\n name\n image(width: 100, height: 100) {\n url\n }\n }\n id\n }\n }\n }\n}\n\nfragment MessageItemFragment on Message {\n id\n content\n created\n extraData\n inReplyTo {\n id\n }\n isRead\n pk\n profile {\n id\n }\n verb\n}\n\nfragment MessagesListFragment on ChatRoom {\n id\n participants {\n totalCount\n }\n isGroup\n unreadMessages {\n count\n markedUnread\n id\n }\n allMessages(first: 20) {\n totalCount\n edges {\n node {\n id\n created\n profile {\n id\n name\n image(height: 32, width: 32) {\n url\n }\n }\n isRead\n ...MessageItemFragment\n __typename\n }\n cursor\n }\n pageInfo {\n hasNextPage\n endCursor\n }\n }\n}\n\nfragment RoomFragment on ChatRoom {\n id\n unreadMessages {\n count\n markedUnread\n id\n }\n lastMessageTime\n lastMessage {\n id\n content\n }\n ...ChatRoomHeaderFragment\n ...MessagesListFragment\n}\n', }, } })() diff --git a/packages/components/__generated__/RoomFragment.graphql.ts b/packages/components/__generated__/RoomFragment.graphql.ts index 1535dfb3..7f56ba2e 100644 --- a/packages/components/__generated__/RoomFragment.graphql.ts +++ b/packages/components/__generated__/RoomFragment.graphql.ts @@ -1,5 +1,5 @@ /** - * @generated SignedSource<<7c1fd5222de7db808df5694f5b913743>> + * @generated SignedSource<<079ffe5d4b40e76be8d5e7e152e4e441>> * @lightSyntaxTransform * @nogrep */ @@ -23,8 +23,8 @@ export type RoomFragment$data = { readonly lastMessageTime: any | null | undefined readonly unreadMessages: | { - readonly count: number | null | undefined - readonly markedUnread: boolean | null | undefined + readonly count: number + readonly markedUnread: boolean } | null | undefined @@ -54,7 +54,7 @@ const node: ReaderFragment = (function () { { alias: null, args: null, - concreteType: 'UnreadMessages', + concreteType: 'UnreadMessageCount', kind: 'LinkedField', name: 'unreadMessages', plural: false, diff --git a/packages/components/__generated__/UnreadChatMutation.graphql.ts b/packages/components/__generated__/UnreadChatMutation.graphql.ts index b9797514..1501215c 100644 --- a/packages/components/__generated__/UnreadChatMutation.graphql.ts +++ b/packages/components/__generated__/UnreadChatMutation.graphql.ts @@ -1,5 +1,5 @@ /** - * @generated SignedSource<<02b8381612f47b357400105930f18b82>> + * @generated SignedSource<<5aa83cdec1d1f95ae9653aaf4fe87d36>> * @lightSyntaxTransform * @nogrep */ @@ -37,8 +37,8 @@ export type UnreadChatMutation$data = { readonly id: string readonly unreadMessages: | { - readonly count: number | null | undefined - readonly markedUnread: boolean | null | undefined + readonly count: number + readonly markedUnread: boolean } | null | undefined @@ -64,98 +64,99 @@ const node: ConcreteRequest = (function () { ], v1 = [ { - alias: null, - args: [ - { - kind: 'Variable', - name: 'input', - variableName: 'input', - }, - ], - concreteType: 'ChatRoomUnreadPayload', - kind: 'LinkedField', - name: 'chatRoomUnread', - plural: false, - selections: [ - { - alias: null, - args: null, - concreteType: 'ChatRoom', - kind: 'LinkedField', - name: 'room', - plural: false, - selections: [ - { - alias: null, - args: null, - kind: 'ScalarField', - name: 'id', - storageKey: null, - }, - { - alias: null, - args: null, - concreteType: 'UnreadMessages', - kind: 'LinkedField', - name: 'unreadMessages', - plural: false, - selections: [ - { - alias: null, - args: null, - kind: 'ScalarField', - name: 'count', - storageKey: null, - }, - { - alias: null, - args: null, - kind: 'ScalarField', - name: 'markedUnread', - storageKey: null, - }, - ], - storageKey: null, - }, - ], - storageKey: null, - }, - { - alias: null, - args: null, - concreteType: 'ErrorType', - kind: 'LinkedField', - name: 'errors', - plural: true, - selections: [ - { - alias: null, - args: null, - kind: 'ScalarField', - name: 'field', - storageKey: null, - }, - { - alias: null, - args: null, - kind: 'ScalarField', - name: 'messages', - storageKey: null, - }, - ], - storageKey: null, - }, - ], - storageKey: null, + kind: 'Variable', + name: 'input', + variableName: 'input', }, - ] + ], + v2 = { + alias: null, + args: null, + kind: 'ScalarField', + name: 'id', + storageKey: null, + }, + v3 = { + alias: null, + args: null, + kind: 'ScalarField', + name: 'count', + storageKey: null, + }, + v4 = { + alias: null, + args: null, + kind: 'ScalarField', + name: 'markedUnread', + storageKey: null, + }, + v5 = { + alias: null, + args: null, + concreteType: 'ErrorType', + kind: 'LinkedField', + name: 'errors', + plural: true, + selections: [ + { + alias: null, + args: null, + kind: 'ScalarField', + name: 'field', + storageKey: null, + }, + { + alias: null, + args: null, + kind: 'ScalarField', + name: 'messages', + storageKey: null, + }, + ], + storageKey: null, + } return { fragment: { argumentDefinitions: v0 /*: any*/, kind: 'Fragment', metadata: null, name: 'UnreadChatMutation', - selections: v1 /*: any*/, + selections: [ + { + alias: null, + args: v1 /*: any*/, + concreteType: 'ChatRoomUnreadPayload', + kind: 'LinkedField', + name: 'chatRoomUnread', + plural: false, + selections: [ + { + alias: null, + args: null, + concreteType: 'ChatRoom', + kind: 'LinkedField', + name: 'room', + plural: false, + selections: [ + v2 /*: any*/, + { + alias: null, + args: null, + concreteType: 'UnreadMessageCount', + kind: 'LinkedField', + name: 'unreadMessages', + plural: false, + selections: [v3 /*: any*/, v4 /*: any*/], + storageKey: null, + }, + ], + storageKey: null, + }, + v5 /*: any*/, + ], + storageKey: null, + }, + ], type: 'Mutation', abstractKey: null, }, @@ -164,15 +165,50 @@ const node: ConcreteRequest = (function () { argumentDefinitions: v0 /*: any*/, kind: 'Operation', name: 'UnreadChatMutation', - selections: v1 /*: any*/, + selections: [ + { + alias: null, + args: v1 /*: any*/, + concreteType: 'ChatRoomUnreadPayload', + kind: 'LinkedField', + name: 'chatRoomUnread', + plural: false, + selections: [ + { + alias: null, + args: null, + concreteType: 'ChatRoom', + kind: 'LinkedField', + name: 'room', + plural: false, + selections: [ + v2 /*: any*/, + { + alias: null, + args: null, + concreteType: 'UnreadMessageCount', + kind: 'LinkedField', + name: 'unreadMessages', + plural: false, + selections: [v3 /*: any*/, v4 /*: any*/, v2 /*: any*/], + storageKey: null, + }, + ], + storageKey: null, + }, + v5 /*: any*/, + ], + storageKey: null, + }, + ], }, params: { - cacheID: '4d9724bd26bcdedc2cc87395d6e1d138', + cacheID: 'a95fee2710fb461b830252379ad489f5', id: null, metadata: {}, name: 'UnreadChatMutation', operationKind: 'mutation', - text: 'mutation UnreadChatMutation(\n $input: ChatRoomUnreadInput!\n) {\n chatRoomUnread(input: $input) {\n room {\n id\n unreadMessages {\n count\n markedUnread\n }\n }\n errors {\n field\n messages\n }\n }\n}\n', + text: 'mutation UnreadChatMutation(\n $input: ChatRoomUnreadInput!\n) {\n chatRoomUnread(input: $input) {\n room {\n id\n unreadMessages {\n count\n markedUnread\n id\n }\n }\n errors {\n field\n messages\n }\n }\n}\n', }, } })() diff --git a/packages/components/__generated__/chatRoomsPaginationQuery.graphql.ts b/packages/components/__generated__/chatRoomsPaginationQuery.graphql.ts index 7ca72a9b..5bd37f15 100644 --- a/packages/components/__generated__/chatRoomsPaginationQuery.graphql.ts +++ b/packages/components/__generated__/chatRoomsPaginationQuery.graphql.ts @@ -1,5 +1,5 @@ /** - * @generated SignedSource<<68537581179b1cf79986f2b2020e3f39>> + * @generated SignedSource<<112b26b3179ebe58ea8ed41bc9a1f8ea>> * @lightSyntaxTransform * @nogrep */ @@ -306,7 +306,7 @@ const node: ConcreteRequest = (function () { { alias: null, args: null, - concreteType: 'UnreadMessages', + concreteType: 'UnreadMessageCount', kind: 'LinkedField', name: 'unreadMessages', plural: false, @@ -325,6 +325,7 @@ const node: ConcreteRequest = (function () { name: 'markedUnread', storageKey: null, }, + v11 /*: any*/, ], storageKey: null, }, @@ -353,6 +354,13 @@ const node: ConcreteRequest = (function () { name: 'title', storageKey: null, }, + { + alias: null, + args: null, + kind: 'ScalarField', + name: 'isGroup', + storageKey: null, + }, { alias: null, args: null, @@ -556,12 +564,12 @@ const node: ConcreteRequest = (function () { ], }, params: { - cacheID: '01ca33ade8d6eb39325613262fee400f', + cacheID: '63c4c537cb2d1d60776168052874da9e', id: null, metadata: {}, name: 'chatRoomsPaginationQuery', operationKind: 'query', - text: 'query chatRoomsPaginationQuery(\n $archived: Boolean = false\n $count: Int = 5\n $cursor: String\n $q: String = null\n $unreadMessages: Boolean = false\n $id: ID!\n) {\n node(id: $id) {\n __typename\n ...RoomsListFragment_3I5PKK\n id\n }\n}\n\nfragment ChatRoomHeaderFragment on ChatRoom {\n image(width: 100, height: 100) {\n url\n }\n title\n participants {\n edges {\n node {\n profile {\n id\n name\n image(width: 100, height: 100) {\n url\n }\n }\n id\n }\n }\n }\n}\n\nfragment MessageItemFragment on Message {\n id\n content\n created\n extraData\n inReplyTo {\n id\n }\n isRead\n pk\n profile {\n id\n }\n verb\n}\n\nfragment MessagesListFragment on ChatRoom {\n id\n participants {\n totalCount\n }\n unreadMessages {\n count\n markedUnread\n }\n allMessages(first: 20) {\n totalCount\n edges {\n node {\n id\n created\n profile {\n id\n name\n image(height: 32, width: 32) {\n url\n }\n }\n isRead\n ...MessageItemFragment\n __typename\n }\n cursor\n }\n pageInfo {\n hasNextPage\n endCursor\n }\n }\n}\n\nfragment RoomFragment on ChatRoom {\n id\n unreadMessages {\n count\n markedUnread\n }\n lastMessageTime\n lastMessage {\n id\n content\n }\n ...ChatRoomHeaderFragment\n ...MessagesListFragment\n}\n\nfragment RoomsListFragment_3I5PKK on ChatRoomsInterface {\n __isChatRoomsInterface: __typename\n chatRooms(first: $count, after: $cursor, q: $q, unreadMessages: $unreadMessages, archived: $archived) {\n edges {\n node {\n id\n ...RoomFragment\n __typename\n }\n cursor\n }\n pageInfo {\n hasNextPage\n endCursor\n }\n }\n id\n}\n', + text: 'query chatRoomsPaginationQuery(\n $archived: Boolean = false\n $count: Int = 5\n $cursor: String\n $q: String = null\n $unreadMessages: Boolean = false\n $id: ID!\n) {\n node(id: $id) {\n __typename\n ...RoomsListFragment_3I5PKK\n id\n }\n}\n\nfragment ChatRoomHeaderFragment on ChatRoom {\n image(width: 100, height: 100) {\n url\n }\n title\n isGroup\n participants {\n edges {\n node {\n profile {\n id\n name\n image(width: 100, height: 100) {\n url\n }\n }\n id\n }\n }\n }\n}\n\nfragment MessageItemFragment on Message {\n id\n content\n created\n extraData\n inReplyTo {\n id\n }\n isRead\n pk\n profile {\n id\n }\n verb\n}\n\nfragment MessagesListFragment on ChatRoom {\n id\n participants {\n totalCount\n }\n isGroup\n unreadMessages {\n count\n markedUnread\n id\n }\n allMessages(first: 20) {\n totalCount\n edges {\n node {\n id\n created\n profile {\n id\n name\n image(height: 32, width: 32) {\n url\n }\n }\n isRead\n ...MessageItemFragment\n __typename\n }\n cursor\n }\n pageInfo {\n hasNextPage\n endCursor\n }\n }\n}\n\nfragment RoomFragment on ChatRoom {\n id\n unreadMessages {\n count\n markedUnread\n id\n }\n lastMessageTime\n lastMessage {\n id\n content\n }\n ...ChatRoomHeaderFragment\n ...MessagesListFragment\n}\n\nfragment RoomsListFragment_3I5PKK on ChatRoomsInterface {\n __isChatRoomsInterface: __typename\n chatRooms(first: $count, after: $cursor, q: $q, unreadMessages: $unreadMessages, archived: $archived) {\n edges {\n node {\n id\n ...RoomFragment\n __typename\n }\n cursor\n }\n pageInfo {\n hasNextPage\n endCursor\n }\n }\n id\n}\n', }, } })() diff --git a/packages/components/__generated__/useMessageCountUpdateSubscription.graphql.ts b/packages/components/__generated__/useMessageCountUpdateSubscription.graphql.ts index 837bb179..695b74f3 100644 --- a/packages/components/__generated__/useMessageCountUpdateSubscription.graphql.ts +++ b/packages/components/__generated__/useMessageCountUpdateSubscription.graphql.ts @@ -1,5 +1,5 @@ /** - * @generated SignedSource<> + * @generated SignedSource<> * @lightSyntaxTransform * @nogrep */ @@ -45,8 +45,8 @@ export type useMessageCountUpdateSubscription$data = { readonly id: string readonly unreadMessages: | { - readonly count: number | null | undefined - readonly markedUnread: boolean | null | undefined + readonly count: number + readonly markedUnread: boolean } | null | undefined @@ -83,165 +83,167 @@ const node: ConcreteRequest = (function () { name: 'profileId', }, ], - v1 = { + v1 = [ + { + kind: 'Variable', + name: 'profileId', + variableName: 'profileId', + }, + ], + v2 = { alias: null, args: null, kind: 'ScalarField', name: 'id', storageKey: null, }, - v2 = [ - { - alias: null, - args: [ - { - kind: 'Variable', - name: 'profileId', - variableName: 'profileId', - }, - ], - concreteType: 'ChatRoomOnMessagesCountUpdate', - kind: 'LinkedField', - name: 'chatRoomOnMessagesCountUpdate', - plural: false, - selections: [ - { - alias: null, - args: null, - concreteType: 'Profile', - kind: 'LinkedField', - name: 'profile', - plural: false, - selections: [ - v1 /*: any*/, - { - alias: null, - args: null, - kind: 'ScalarField', - name: 'unreadMessagesCount', - storageKey: null, - }, - { - alias: null, - args: null, - concreteType: 'ChatRoomConnection', - kind: 'LinkedField', - name: 'chatRooms', - plural: false, - selections: [ - { - alias: null, - args: null, - kind: 'ScalarField', - name: 'totalCount', - storageKey: null, - }, - { - alias: null, - args: null, - concreteType: 'ChatRoomEdge', - kind: 'LinkedField', - name: 'edges', - plural: true, - selections: [ - { - alias: null, - args: null, - concreteType: 'ChatRoom', - kind: 'LinkedField', - name: 'node', - plural: false, - selections: [ - v1 /*: any*/, - { - alias: null, - args: null, - concreteType: 'UnreadMessages', - kind: 'LinkedField', - name: 'unreadMessages', - plural: false, - selections: [ - { - alias: null, - args: null, - kind: 'ScalarField', - name: 'count', - storageKey: null, - }, - { - alias: null, - args: null, - kind: 'ScalarField', - name: 'markedUnread', - storageKey: null, - }, - ], - storageKey: null, - }, - { - alias: null, - args: null, - concreteType: 'MessageConnection', - kind: 'LinkedField', - name: 'allMessages', - plural: false, - selections: [ - { - alias: null, - args: null, - concreteType: 'MessageEdge', - kind: 'LinkedField', - name: 'edges', - plural: true, - selections: [ - { - alias: null, - args: null, - concreteType: 'Message', - kind: 'LinkedField', - name: 'node', - plural: false, - selections: [ - v1 /*: any*/, - { - alias: null, - args: null, - kind: 'ScalarField', - name: 'isRead', - storageKey: null, - }, - ], - storageKey: null, - }, - ], - storageKey: null, - }, - ], - storageKey: null, - }, - ], - storageKey: null, - }, - ], - storageKey: null, - }, - ], - storageKey: null, - }, - ], - storageKey: null, - }, - ], - storageKey: null, - }, - ] + v3 = { + alias: null, + args: null, + kind: 'ScalarField', + name: 'unreadMessagesCount', + storageKey: null, + }, + v4 = { + alias: null, + args: null, + kind: 'ScalarField', + name: 'totalCount', + storageKey: null, + }, + v5 = { + alias: null, + args: null, + kind: 'ScalarField', + name: 'count', + storageKey: null, + }, + v6 = { + alias: null, + args: null, + kind: 'ScalarField', + name: 'markedUnread', + storageKey: null, + }, + v7 = { + alias: null, + args: null, + concreteType: 'MessageConnection', + kind: 'LinkedField', + name: 'allMessages', + plural: false, + selections: [ + { + alias: null, + args: null, + concreteType: 'MessageEdge', + kind: 'LinkedField', + name: 'edges', + plural: true, + selections: [ + { + alias: null, + args: null, + concreteType: 'Message', + kind: 'LinkedField', + name: 'node', + plural: false, + selections: [ + v2 /*: any*/, + { + alias: null, + args: null, + kind: 'ScalarField', + name: 'isRead', + storageKey: null, + }, + ], + storageKey: null, + }, + ], + storageKey: null, + }, + ], + storageKey: null, + } return { fragment: { argumentDefinitions: v0 /*: any*/, kind: 'Fragment', metadata: null, name: 'useMessageCountUpdateSubscription', - selections: v2 /*: any*/, + selections: [ + { + alias: null, + args: v1 /*: any*/, + concreteType: 'ChatRoomOnMessagesCountUpdate', + kind: 'LinkedField', + name: 'chatRoomOnMessagesCountUpdate', + plural: false, + selections: [ + { + alias: null, + args: null, + concreteType: 'Profile', + kind: 'LinkedField', + name: 'profile', + plural: false, + selections: [ + v2 /*: any*/, + v3 /*: any*/, + { + alias: null, + args: null, + concreteType: 'ChatRoomConnection', + kind: 'LinkedField', + name: 'chatRooms', + plural: false, + selections: [ + v4 /*: any*/, + { + alias: null, + args: null, + concreteType: 'ChatRoomEdge', + kind: 'LinkedField', + name: 'edges', + plural: true, + selections: [ + { + alias: null, + args: null, + concreteType: 'ChatRoom', + kind: 'LinkedField', + name: 'node', + plural: false, + selections: [ + v2 /*: any*/, + { + alias: null, + args: null, + concreteType: 'UnreadMessageCount', + kind: 'LinkedField', + name: 'unreadMessages', + plural: false, + selections: [v5 /*: any*/, v6 /*: any*/], + storageKey: null, + }, + v7 /*: any*/, + ], + storageKey: null, + }, + ], + storageKey: null, + }, + ], + storageKey: null, + }, + ], + storageKey: null, + }, + ], + storageKey: null, + }, + ], type: 'Subscription', abstractKey: null, }, @@ -250,15 +252,86 @@ const node: ConcreteRequest = (function () { argumentDefinitions: v0 /*: any*/, kind: 'Operation', name: 'useMessageCountUpdateSubscription', - selections: v2 /*: any*/, + selections: [ + { + alias: null, + args: v1 /*: any*/, + concreteType: 'ChatRoomOnMessagesCountUpdate', + kind: 'LinkedField', + name: 'chatRoomOnMessagesCountUpdate', + plural: false, + selections: [ + { + alias: null, + args: null, + concreteType: 'Profile', + kind: 'LinkedField', + name: 'profile', + plural: false, + selections: [ + v2 /*: any*/, + v3 /*: any*/, + { + alias: null, + args: null, + concreteType: 'ChatRoomConnection', + kind: 'LinkedField', + name: 'chatRooms', + plural: false, + selections: [ + v4 /*: any*/, + { + alias: null, + args: null, + concreteType: 'ChatRoomEdge', + kind: 'LinkedField', + name: 'edges', + plural: true, + selections: [ + { + alias: null, + args: null, + concreteType: 'ChatRoom', + kind: 'LinkedField', + name: 'node', + plural: false, + selections: [ + v2 /*: any*/, + { + alias: null, + args: null, + concreteType: 'UnreadMessageCount', + kind: 'LinkedField', + name: 'unreadMessages', + plural: false, + selections: [v5 /*: any*/, v6 /*: any*/, v2 /*: any*/], + storageKey: null, + }, + v7 /*: any*/, + ], + storageKey: null, + }, + ], + storageKey: null, + }, + ], + storageKey: null, + }, + ], + storageKey: null, + }, + ], + storageKey: null, + }, + ], }, params: { - cacheID: '14333857437b14661f927afc905ac42f', + cacheID: '82134f453c19e9fc16ae77d236900477', id: null, metadata: {}, name: 'useMessageCountUpdateSubscription', operationKind: 'subscription', - text: 'subscription useMessageCountUpdateSubscription(\n $profileId: ID!\n) {\n chatRoomOnMessagesCountUpdate(profileId: $profileId) {\n profile {\n id\n unreadMessagesCount\n chatRooms {\n totalCount\n edges {\n node {\n id\n unreadMessages {\n count\n markedUnread\n }\n allMessages {\n edges {\n node {\n id\n isRead\n }\n }\n }\n }\n }\n }\n }\n }\n}\n', + text: 'subscription useMessageCountUpdateSubscription(\n $profileId: ID!\n) {\n chatRoomOnMessagesCountUpdate(profileId: $profileId) {\n profile {\n id\n unreadMessagesCount\n chatRooms {\n totalCount\n edges {\n node {\n id\n unreadMessages {\n count\n markedUnread\n id\n }\n allMessages {\n edges {\n node {\n id\n isRead\n }\n }\n }\n }\n }\n }\n }\n }\n}\n', }, } })() diff --git a/packages/components/__generated__/useRoomListSubscription.graphql.ts b/packages/components/__generated__/useRoomListSubscription.graphql.ts index 28a321fe..a54d5d0e 100644 --- a/packages/components/__generated__/useRoomListSubscription.graphql.ts +++ b/packages/components/__generated__/useRoomListSubscription.graphql.ts @@ -1,5 +1,5 @@ /** - * @generated SignedSource<<087b933a84e193988f8e6fc01a359ed2>> + * @generated SignedSource<> * @lightSyntaxTransform * @nogrep */ @@ -206,7 +206,7 @@ const node: ConcreteRequest = (function () { { alias: null, args: null, - concreteType: 'UnreadMessages', + concreteType: 'UnreadMessageCount', kind: 'LinkedField', name: 'unreadMessages', plural: false, @@ -225,6 +225,7 @@ const node: ConcreteRequest = (function () { name: 'markedUnread', storageKey: null, }, + v3 /*: any*/, ], storageKey: null, }, @@ -253,6 +254,13 @@ const node: ConcreteRequest = (function () { name: 'title', storageKey: null, }, + { + alias: null, + args: null, + kind: 'ScalarField', + name: 'isGroup', + storageKey: null, + }, { alias: null, args: null, @@ -489,12 +497,12 @@ const node: ConcreteRequest = (function () { ], }, params: { - cacheID: '950110109bb9b8a50fe13efe86c1b32e', + cacheID: 'd2705656d5200edab081b2548b59e7f7', id: null, metadata: {}, name: 'useRoomListSubscription', operationKind: 'subscription', - text: 'subscription useRoomListSubscription(\n $profileId: ID!\n) {\n chatRoomOnRoomUpdate(profileId: $profileId) {\n room {\n node {\n id\n ...RoomFragment\n }\n }\n }\n}\n\nfragment ChatRoomHeaderFragment on ChatRoom {\n image(width: 100, height: 100) {\n url\n }\n title\n participants {\n edges {\n node {\n profile {\n id\n name\n image(width: 100, height: 100) {\n url\n }\n }\n id\n }\n }\n }\n}\n\nfragment MessageItemFragment on Message {\n id\n content\n created\n extraData\n inReplyTo {\n id\n }\n isRead\n pk\n profile {\n id\n }\n verb\n}\n\nfragment MessagesListFragment on ChatRoom {\n id\n participants {\n totalCount\n }\n unreadMessages {\n count\n markedUnread\n }\n allMessages(first: 20) {\n totalCount\n edges {\n node {\n id\n created\n profile {\n id\n name\n image(height: 32, width: 32) {\n url\n }\n }\n isRead\n ...MessageItemFragment\n __typename\n }\n cursor\n }\n pageInfo {\n hasNextPage\n endCursor\n }\n }\n}\n\nfragment RoomFragment on ChatRoom {\n id\n unreadMessages {\n count\n markedUnread\n }\n lastMessageTime\n lastMessage {\n id\n content\n }\n ...ChatRoomHeaderFragment\n ...MessagesListFragment\n}\n', + text: 'subscription useRoomListSubscription(\n $profileId: ID!\n) {\n chatRoomOnRoomUpdate(profileId: $profileId) {\n room {\n node {\n id\n ...RoomFragment\n }\n }\n }\n}\n\nfragment ChatRoomHeaderFragment on ChatRoom {\n image(width: 100, height: 100) {\n url\n }\n title\n isGroup\n participants {\n edges {\n node {\n profile {\n id\n name\n image(width: 100, height: 100) {\n url\n }\n }\n id\n }\n }\n }\n}\n\nfragment MessageItemFragment on Message {\n id\n content\n created\n extraData\n inReplyTo {\n id\n }\n isRead\n pk\n profile {\n id\n }\n verb\n}\n\nfragment MessagesListFragment on ChatRoom {\n id\n participants {\n totalCount\n }\n isGroup\n unreadMessages {\n count\n markedUnread\n id\n }\n allMessages(first: 20) {\n totalCount\n edges {\n node {\n id\n created\n profile {\n id\n name\n image(height: 32, width: 32) {\n url\n }\n }\n isRead\n ...MessageItemFragment\n __typename\n }\n cursor\n }\n pageInfo {\n hasNextPage\n endCursor\n }\n }\n}\n\nfragment RoomFragment on ChatRoom {\n id\n unreadMessages {\n count\n markedUnread\n id\n }\n lastMessageTime\n lastMessage {\n id\n content\n }\n ...ChatRoomHeaderFragment\n ...MessagesListFragment\n}\n', }, } })() diff --git a/packages/components/modules/messages/ChatRoom/ChatRoomHeader/utils.ts b/packages/components/modules/messages/ChatRoom/ChatRoomHeader/utils.ts index 648508c4..7c9aedba 100644 --- a/packages/components/modules/messages/ChatRoom/ChatRoomHeader/utils.ts +++ b/packages/components/modules/messages/ChatRoom/ChatRoomHeader/utils.ts @@ -1,8 +1,8 @@ import { ChatRoomHeaderFragment$data } from '../../../../__generated__/ChatRoomHeaderFragment.graphql' -import { getParticipantCount, isGroupChat } from '../../utils' +import { getParticipantCount } from '../../utils' export const getSubtitle = (roomHeader: ChatRoomHeaderFragment$data) => { - if (isGroupChat(roomHeader)) { + if (roomHeader.isGroup) { const participantCount = getParticipantCount(roomHeader) if (participantCount !== undefined) { return `${participantCount} member${participantCount !== 1 ? 's' : ''}` diff --git a/packages/components/modules/messages/ChatRoomsList/index.tsx b/packages/components/modules/messages/ChatRoomsList/index.tsx index 79658a81..9af5c142 100644 --- a/packages/components/modules/messages/ChatRoomsList/index.tsx +++ b/packages/components/modules/messages/ChatRoomsList/index.tsx @@ -111,7 +111,7 @@ const ChatRoomsList: FC = ({ const renderTabLabel = (tabValue: ChatTabValues) => ( - {CHAT_TAB_LABEL[CHAT_TAB_VALUES[tabValue]]} + {CHAT_TAB_LABEL[tabValue]} {isRefetchPending && tab === tabValue && } diff --git a/packages/components/modules/messages/CreateChatRoomList/index.tsx b/packages/components/modules/messages/CreateChatRoomList/index.tsx index 6f6658b3..6fae5a5a 100644 --- a/packages/components/modules/messages/CreateChatRoomList/index.tsx +++ b/packages/components/modules/messages/CreateChatRoomList/index.tsx @@ -15,11 +15,12 @@ import { useForm } from 'react-hook-form' import { Virtuoso } from 'react-virtuoso' import { useAllProfilesList } from '../../profiles/graphql/queries/AllProfilesList' +import EmptyProfilesListState from '../EmptyProfilesListState' import SearchNotFoundState from '../SearchNotFoundState' +import { ProfileEdge, ProfileNode } from '../types' import DefaultChatRoomListItem from './ChatRoomListItem' -import EmptyProfilesListState from './EmptyProfilesListState' import { GroupChatContainer, MainContainer, SearchbarContainer } from './styled' -import { CreateChatRoomListProps, ProfileEdge, ProfileNode } from './types' +import { CreateChatRoomListProps } from './types' const CreateChatRoomList: FC = ({ allProfilesRef, @@ -29,6 +30,7 @@ const CreateChatRoomList: FC = ({ SearchbarProps = {}, VirtuosoProps = {}, setIsInExistingChatRoomsView, + setIsInGroupChatCreation, }) => { const { data: { allProfiles }, @@ -61,7 +63,7 @@ const CreateChatRoomList: FC = ({ allProfiles?.edges .filter((edge: ProfileEdge) => edge?.node && edge.node.id !== currentProfile?.id) .map((edge: ProfileEdge) => edge?.node) || [], - [allProfiles], + [allProfiles, currentProfile?.id], ) const renderLoadingState = () => { @@ -100,7 +102,8 @@ const CreateChatRoomList: FC = ({ renderItem(item)} - style={{ scrollbarWidth: 'none' }} + // TODO: look for a better way to calculate the height, it doesn't consider different types of headers + style={{ scrollbarWidth: 'none', maxHeight: 'calc(100vh - 72px - 57px - 61px - 72px)' }} components={{ Footer: renderLoadingState, }} @@ -127,7 +130,11 @@ const CreateChatRoomList: FC = ({ /> {/* TODO: Group chat creation click handler */} - {}}> + { + setIsInGroupChatCreation(true) + }} + > ({ gridTemplateColumns: '48px auto', gap: theme.spacing(1.5), padding: `${theme.spacing(1.5)} ${theme.spacing(2.5)}`, + cursor: 'pointer', [theme.breakpoints.down('sm')]: { padding: `${theme.spacing(1.5)} ${theme.spacing(1.5)}`, }, diff --git a/packages/components/modules/messages/CreateChatRoomList/types.ts b/packages/components/modules/messages/CreateChatRoomList/types.ts index 41eda573..4b0b47ce 100644 --- a/packages/components/modules/messages/CreateChatRoomList/types.ts +++ b/packages/components/modules/messages/CreateChatRoomList/types.ts @@ -4,15 +4,9 @@ import { SearchbarProps } from '@baseapp-frontend/design-system' import { VirtuosoProps } from 'react-virtuoso' -import { AllProfilesListFragment$data } from '../../../__generated__/AllProfilesListFragment.graphql' import { ChatRoomsQuery$data } from '../../../__generated__/ChatRoomsQuery.graphql' import { ChatRoomListItemProps } from './ChatRoomListItem/types' -type AllProfiles = NonNullable -type AllProfilesEdges = AllProfiles['edges'] -export type ProfileEdge = AllProfilesEdges[number] -export type ProfileNode = NonNullable['node'] - export interface CreateChatRoomListProps extends PropsWithChildren { allProfilesRef: ChatRoomsQuery$data Searchbar?: FC @@ -21,4 +15,5 @@ export interface CreateChatRoomListProps extends PropsWithChildren { ChatRoomListItemProps?: Partial VirtuosoProps?: Partial> setIsInExistingChatRoomsView: Dispatch> + setIsInGroupChatCreation: Dispatch> } diff --git a/packages/components/modules/messages/CreateGroup/ConnectionsList/index.tsx b/packages/components/modules/messages/CreateGroup/ConnectionsList/index.tsx new file mode 100644 index 00000000..0b6e1745 --- /dev/null +++ b/packages/components/modules/messages/CreateGroup/ConnectionsList/index.tsx @@ -0,0 +1,62 @@ +'use client' + +import { FC } from 'react' + +import { LoadingState } from '@baseapp-frontend/design-system' + +import { Box } from '@mui/material' +import { Virtuoso } from 'react-virtuoso' + +import DefaultEmptyProfilesListState from '../../EmptyProfilesListState' +import DefaultSearchNotFoundState from '../../SearchNotFoundState' +import { ConnectionsListProps } from './types' + +const ConnectionsList: FC = ({ + searchValue, + profiles = [], + isPending, + isLoadingNext, + hasNext, + loadNext, + renderItem, + VirtuosoProps = {}, + EmptyProfilesListState = DefaultEmptyProfilesListState, + SearchNotFoundState = DefaultSearchNotFoundState, +}) => { + const renderLoadingState = () => { + if (!isLoadingNext) return + + return ( + + ) + } + + const emptyProfilesList = profiles.length === 0 + + if (!isPending && searchValue && emptyProfilesList) return + + if (!isPending && emptyProfilesList) return + + return ( + renderItem(item)} + style={{ scrollbarWidth: 'none', maxHeight: '250px' }} + components={{ + Footer: renderLoadingState, + }} + endReached={() => { + if (hasNext) { + loadNext(5) + } + }} + {...VirtuosoProps} + /> + ) +} + +export default ConnectionsList diff --git a/packages/components/modules/messages/CreateGroup/ConnectionsList/styled.tsx b/packages/components/modules/messages/CreateGroup/ConnectionsList/styled.tsx new file mode 100644 index 00000000..a9a761b6 --- /dev/null +++ b/packages/components/modules/messages/CreateGroup/ConnectionsList/styled.tsx @@ -0,0 +1,17 @@ +import { Box } from '@mui/material' +import { styled } from '@mui/material/styles' + +export const MainContainer = styled(Box)(({ theme }) => ({ + alignItems: 'center', + display: 'grid', + width: '100%', + height: '100%', + maxWidth: '390px', + gridTemplateColumns: '48px auto min-content', + gap: theme.spacing(1.5), + padding: `${theme.spacing(1.5)} ${theme.spacing(2.5)}`, + [theme.breakpoints.down('sm')]: { + maxWidth: '600px', + padding: `${theme.spacing(1.5)} ${theme.spacing(1.5)}`, + }, +})) diff --git a/packages/components/modules/messages/CreateGroup/ConnectionsList/types.ts b/packages/components/modules/messages/CreateGroup/ConnectionsList/types.ts new file mode 100644 index 00000000..95eb94e0 --- /dev/null +++ b/packages/components/modules/messages/CreateGroup/ConnectionsList/types.ts @@ -0,0 +1,20 @@ +import { FC } from 'react' + +import { LoadMoreFn } from 'react-relay' +import { VirtuosoProps } from 'react-virtuoso' + +import { AllProfilesListPaginationQuery } from '../../../../__generated__/AllProfilesListPaginationQuery.graphql' +import { ProfileNode } from '../../types' + +export interface ConnectionsListProps { + VirtuosoProps?: Partial> + searchValue?: string | null + profiles?: ProfileNode[] + isPending: boolean + isLoadingNext: boolean + hasNext: boolean + loadNext: LoadMoreFn + renderItem: (profile: ProfileNode, isMember?: boolean) => JSX.Element | null + SearchNotFoundState?: FC + EmptyProfilesListState?: FC +} diff --git a/packages/components/modules/messages/CreateGroup/ProfileCard/index.tsx b/packages/components/modules/messages/CreateGroup/ProfileCard/index.tsx new file mode 100644 index 00000000..6fe2c617 --- /dev/null +++ b/packages/components/modules/messages/CreateGroup/ProfileCard/index.tsx @@ -0,0 +1,57 @@ +'use client' + +import { FC } from 'react' + +import { AvatarWithPlaceholder } from '@baseapp-frontend/design-system' + +import { Box, Button, Typography } from '@mui/material' +import { useFragment } from 'react-relay' + +import { ProfileItemFragment } from '../../../profiles/graphql/queries/ProfileItem' +import { MainContainer } from './styled' +import { ProfileCardProps } from './types' + +const ProfileCard: FC = ({ + profile: profileRef, + handleAddMember, + handleRemoveMember, + isMember = false, +}) => { + const { id, image, name, urlPath } = useFragment(ProfileItemFragment, profileRef) + + return ( + + + + {name} + + {urlPath?.path && `@${urlPath.path}`} + + + + + ) +} + +export default ProfileCard diff --git a/packages/components/modules/messages/CreateGroup/ProfileCard/styled.tsx b/packages/components/modules/messages/CreateGroup/ProfileCard/styled.tsx new file mode 100644 index 00000000..a9a761b6 --- /dev/null +++ b/packages/components/modules/messages/CreateGroup/ProfileCard/styled.tsx @@ -0,0 +1,17 @@ +import { Box } from '@mui/material' +import { styled } from '@mui/material/styles' + +export const MainContainer = styled(Box)(({ theme }) => ({ + alignItems: 'center', + display: 'grid', + width: '100%', + height: '100%', + maxWidth: '390px', + gridTemplateColumns: '48px auto min-content', + gap: theme.spacing(1.5), + padding: `${theme.spacing(1.5)} ${theme.spacing(2.5)}`, + [theme.breakpoints.down('sm')]: { + maxWidth: '600px', + padding: `${theme.spacing(1.5)} ${theme.spacing(1.5)}`, + }, +})) diff --git a/packages/components/modules/messages/CreateGroup/ProfileCard/types.ts b/packages/components/modules/messages/CreateGroup/ProfileCard/types.ts new file mode 100644 index 00000000..8241a08d --- /dev/null +++ b/packages/components/modules/messages/CreateGroup/ProfileCard/types.ts @@ -0,0 +1,9 @@ +import { ProfileItemFragment$key } from '../../../../__generated__/ProfileItemFragment.graphql' + +export interface ProfileCardProps { + profile: ProfileItemFragment$key + // TODO: type this better + handleAddMember: (profile: any) => void + handleRemoveMember: (profile: any) => void + isMember?: boolean +} diff --git a/packages/components/modules/messages/CreateGroup/constants.ts b/packages/components/modules/messages/CreateGroup/constants.ts new file mode 100644 index 00000000..9fe844fb --- /dev/null +++ b/packages/components/modules/messages/CreateGroup/constants.ts @@ -0,0 +1,28 @@ +import { z } from 'zod' + +import { CreatGroupUpload } from './types' + +export const FORM_VALUE: Record = { + title: 'title', + participants: 'participants', + image: 'image', +} + +export const DEFAULT_FORM_VALUES: CreatGroupUpload = { + title: '', + participants: [], + image: '', +} + +export const DEFAULT_FORM_VALIDATION = z.object({ + [FORM_VALUE.title]: z.string().min(1, { message: 'Please enter a title' }), + [FORM_VALUE.participants]: z + .array(z.any()) + .min(1, { message: 'Please select at least one member' }), + [FORM_VALUE.image]: z.any(), +}) + +// .svg is not supported by the backend, so better not use 'image/*' +export const DEFAULT_IMAGE_FORMATS = 'image/png, image/gif, image/jpeg' +// use "DEFAULT_IMAGE_MAX_SIZE = undefined" to allow uploads of any size +export const DEFAULT_IMAGE_MAX_SIZE = 10 * 1024 * 1024 diff --git a/packages/components/modules/messages/CreateGroup/index.tsx b/packages/components/modules/messages/CreateGroup/index.tsx new file mode 100644 index 00000000..f2736376 --- /dev/null +++ b/packages/components/modules/messages/CreateGroup/index.tsx @@ -0,0 +1,344 @@ +'use client' + +import { ChangeEventHandler, FC, useMemo, useTransition } from 'react' + +import { useCurrentProfile } from '@baseapp-frontend/authentication' +import { + CheckMarkIcon, + CircledAvatar, + CloseIcon, + Searchbar as DefaultSearchbar, + FileUploadButton, + IconButton, + TextField, +} from '@baseapp-frontend/design-system' +import { filterDirtyValues, setFormRelayErrors, useNotification } from '@baseapp-frontend/utils' + +import { zodResolver } from '@hookform/resolvers/zod' +import { LoadingButton } from '@mui/lab' +import { Box, Typography, useTheme } from '@mui/material' +import { useForm } from 'react-hook-form' +import { ConnectionHandler } from 'relay-runtime' + +import { useAllProfilesList } from '../../profiles/graphql/queries/AllProfilesList' +import { useChatRoom } from '../context' +import { useCreateChatRoomMutation } from '../graphql/mutations/CreateChatRoom' +import { ProfileNode } from '../types' +import DefaultConnectionsList from './ConnectionsList' +import DefaultProfileCard from './ProfileCard' +import { + DEFAULT_FORM_VALIDATION, + DEFAULT_FORM_VALUES, + DEFAULT_IMAGE_FORMATS, + DEFAULT_IMAGE_MAX_SIZE, + FORM_VALUE, +} from './constants' +import { + HeaderContainer, + ProfilesContainer, + SearchbarContainer, + UploadProfileContainer, +} from './styled' +import { CreatGroupUpload, CreateGroupProps } from './types' +import { getImageUrl } from './utils' + +const CreateGroup: FC = ({ + allProfilesRef, + ProfileCard = DefaultProfileCard, + ProfileCardProps = {}, + Searchbar = DefaultSearchbar, + SearchbarProps = {}, + ConnectionsList = DefaultConnectionsList, + ConnectionsListProps = {}, + setIsInGroupChatCreation, + setIsInExistingChatRoomsView, +}) => { + const theme = useTheme() + const { sendToast } = useNotification() + const { + data: { allProfiles }, + loadNext, + isLoadingNext, + hasNext, + refetch, + } = useAllProfilesList(allProfilesRef) + const [isPending, startTransition] = useTransition() + const { + control: searchControl, + reset: searchReset, + watch: searchWatch, + } = useForm({ defaultValues: { search: '' } }) + + const formReturn = useForm({ + defaultValues: DEFAULT_FORM_VALUES, + resolver: zodResolver(DEFAULT_FORM_VALIDATION), + mode: 'onBlur', + }) + + const { + control, + setValue, + watch, + getFieldState, + clearErrors, + reset, + handleSubmit, + formState: { isValid, isDirty, dirtyFields }, + trigger, + } = formReturn + + const { currentProfile } = useCurrentProfile() + + const [commit, isMutationInFlight] = useCreateChatRoomMutation() + const { setChatRoom } = useChatRoom() + + const onSubmit = handleSubmit((data: CreatGroupUpload) => { + const dirtyValues = filterDirtyValues({ values: data, dirtyFields }) + const { title, participants, image } = data + const participantsIds = participants.map((member) => member.id) + const uploadables: { image?: File | Blob } = {} + if ('image' in dirtyValues && image && typeof image !== 'string') { + uploadables.image = image + } + + commit({ + variables: { + input: { + title, + isGroup: true, + profileId: currentProfile?.id as string, + participants: participantsIds, + }, + connections: [ + // TODO: add filter handling (for now we can default 'unreadMessages' to false) + ConnectionHandler.getConnectionID(currentProfile?.id as string, 'roomsList_chatRooms', { + unreadMessages: false, + }), + ], + }, + uploadables, + onCompleted: (response) => { + const errors = response?.chatRoomCreate?.errors + if (errors) { + sendToast('Something went wrong', { type: 'error' }) + setFormRelayErrors(formReturn, errors) + } else { + setChatRoom({ id: response?.chatRoomCreate?.room?.node?.id }) + setIsInExistingChatRoomsView(true) + } + }, + }) + reset({}, { keepValues: true }) + setIsInGroupChatCreation(false) + }) + const watchImage = watch(FORM_VALUE.image) + const imageUrl = getImageUrl(watchImage) + const remotePatternsHostName = process.env.NEXT_PUBLIC_REMOTE_PATTERNS_HOSTNAME || '' // To get this working in staging/prod, change NEXT_PUBLIC_REMOTE_PATTERNS_HOSTNAME to the media host being used (e.g.: digitalocean, aws, etc) + const hasUploadedImage = remotePatternsHostName && imageUrl.includes(remotePatternsHostName) + + const handleSearchChange: ChangeEventHandler = (e) => { + const value = e.target.value || '' + startTransition(() => { + refetch({ q: value }) + }) + } + + const handleSearchClear = () => { + startTransition(() => { + searchReset() + refetch({ q: '' }) + }) + } + + const participants = watch(FORM_VALUE.participants) as ProfileNode[] + + const profiles = useMemo( + () => + allProfiles?.edges + .filter( + (edge) => + edge?.node && + edge?.node.id !== currentProfile?.id && + !participants.some((member) => member?.id === edge?.node?.id), + ) + .map((edge) => edge?.node) || [], + [allProfiles, participants, currentProfile?.id], + ) + + const handleAddMember = (profile: ProfileNode) => { + setValue(FORM_VALUE.participants, [...participants, profile], { + shouldValidate: true, + shouldDirty: true, + shouldTouch: true, + }) + } + + const handleRemoveMember = (profile: ProfileNode) => { + setValue( + FORM_VALUE.participants, + participants.filter((member) => member?.id !== profile?.id), + { + shouldValidate: true, + shouldDirty: true, + shouldTouch: true, + }, + ) + } + + const renderItem = (profile: ProfileNode, isMember = false) => { + if (!profile) return null + return ( + + ) + } + + const handleRemoveImage = (type: keyof CreatGroupUpload) => { + clearErrors(type) + setValue(type, null, { + shouldValidate: false, + shouldDirty: true, + shouldTouch: true, + }) + } + + const isCreateButtonDisabled = !isValid || !isDirty || isMutationInFlight + + return ( + <> + + + { + setIsInGroupChatCreation(false) + }} + aria-label="cancel group creation" + > + + + + New Group + + { + onSubmit() + }} + > + + + + + + + {getFieldState('image').error && ( +
+ + {getFieldState('image').error!.message} + +
+ )} + + {watchImage && ( + handleRemoveImage(FORM_VALUE.image)} + > + Remove + + )} +
+ { + trigger(FORM_VALUE.title) + }} + /> +
+
+ + + + + + + + Members + + + {participants.map((member) => renderItem(member, true))} + + + + + Connections + + + + + + + ) +} + +export default CreateGroup diff --git a/packages/components/modules/messages/CreateGroup/styled.tsx b/packages/components/modules/messages/CreateGroup/styled.tsx new file mode 100644 index 00000000..9c8e0dd4 --- /dev/null +++ b/packages/components/modules/messages/CreateGroup/styled.tsx @@ -0,0 +1,37 @@ +import { Box } from '@mui/material' +import { styled } from '@mui/material/styles' + +export const SearchbarContainer = styled(Box)(({ theme }) => ({ + padding: `${theme.spacing(2)} ${theme.spacing(2.5)} 0`, + [theme.breakpoints.down('sm')]: { + padding: `${theme.spacing(2)} ${theme.spacing(1.5)} 0`, + }, +})) + +export const HeaderContainer = styled(Box)(({ theme }) => ({ + display: 'grid', + gridTemplateColumns: '24px auto 24px', + alignItems: 'center', + width: '100%', + padding: theme.spacing(2), + borderBottom: `1px solid ${theme.palette.divider}`, +})) + +export const UploadProfileContainer = styled(Box)(({ theme }) => ({ + display: 'grid', + gridTemplateRows: '1fr min-content', + justifyContent: 'center', + gap: theme.spacing(2), + padding: theme.spacing(1.5), +})) + +export const ProfilesContainer = styled(Box)(({ theme }) => ({ + height: '100%', + width: '100%', + overflowY: 'auto', + scrollbarWidth: 'none', + [theme.breakpoints.down('sm')]: { + // TODO: look for a better way to calculate the height, it doesn't consider different types of headers + height: `calc(100vh - 72px - 57px - 305px)`, + }, +})) diff --git a/packages/components/modules/messages/CreateGroup/types.ts b/packages/components/modules/messages/CreateGroup/types.ts new file mode 100644 index 00000000..2b69a5a1 --- /dev/null +++ b/packages/components/modules/messages/CreateGroup/types.ts @@ -0,0 +1,25 @@ +import { Dispatch, FC, PropsWithChildren, SetStateAction } from 'react' + +import { SearchbarProps } from '@baseapp-frontend/design-system' + +import { ChatRoomsQuery$data } from '../../../__generated__/ChatRoomsQuery.graphql' +import { ConnectionsListProps } from './ConnectionsList/types' +import { ProfileCardProps } from './ProfileCard/types' + +export interface CreateGroupProps extends PropsWithChildren { + allProfilesRef: ChatRoomsQuery$data + Searchbar?: FC + SearchbarProps?: Partial + ProfileCard?: FC + ProfileCardProps?: Partial + ConnectionsList?: FC + ConnectionsListProps?: Partial + setIsInGroupChatCreation: Dispatch> + setIsInExistingChatRoomsView: Dispatch> +} + +export interface CreatGroupUpload { + title: string + participants: any[] + image?: string | File | Blob | null +} diff --git a/packages/components/modules/messages/CreateGroup/utils.ts b/packages/components/modules/messages/CreateGroup/utils.ts new file mode 100644 index 00000000..a3abf948 --- /dev/null +++ b/packages/components/modules/messages/CreateGroup/utils.ts @@ -0,0 +1,5 @@ +export const getImageUrl = (image?: string | File | Blob | string[] | MediaSource | null) => { + if (typeof image === 'string') return image + if (image instanceof Blob) return URL.createObjectURL(image) + return '' +} diff --git a/packages/components/modules/messages/CreateChatRoomList/EmptyProfilesListState/index.tsx b/packages/components/modules/messages/EmptyProfilesListState/index.tsx similarity index 100% rename from packages/components/modules/messages/CreateChatRoomList/EmptyProfilesListState/index.tsx rename to packages/components/modules/messages/EmptyProfilesListState/index.tsx diff --git a/packages/components/modules/messages/MessagesList/MessagesGroup/index.tsx b/packages/components/modules/messages/MessagesList/MessagesGroup/index.tsx index 2705444c..3449e4aa 100644 --- a/packages/components/modules/messages/MessagesList/MessagesGroup/index.tsx +++ b/packages/components/modules/messages/MessagesList/MessagesGroup/index.tsx @@ -7,10 +7,7 @@ import { datesDontHaveSameDay } from '@baseapp-frontend/utils' import { Box, Divider, Typography, useTheme } from '@mui/material' import { DateTime } from 'luxon' -import { - MAXIMUM_DIFF_TO_GROUP_MESSAGES_CREATED_TIME, - MINIMUM_AMOUNT_OF_PARTICIPANTS_TO_SHOW_ROOM_TITLE, -} from '../../constants' +import { MAXIMUM_DIFF_TO_GROUP_MESSAGES_CREATED_TIME } from '../../constants' import DefaultMessageItem from './MessageItem' import Timestamp from './Timestamp' import { DateGroupTypography } from './styled' @@ -23,7 +20,7 @@ const MessagesGroup: FC = ({ hasNext, message, messageIndex, - roomParticipantsCount, + isGroup = false, MessageItem = DefaultMessageItem, MessageItemProps = {}, }) => { @@ -125,10 +122,7 @@ const MessagesGroup: FC = ({ const flexAlignments = isOwnMessage ? 'flex-end' : 'flex-start' const canShowAvatar = isFirstGroupedMessage && !isOwnMessage - const canShowName = - canShowAvatar && - roomParticipantsCount && - roomParticipantsCount >= MINIMUM_AMOUNT_OF_PARTICIPANTS_TO_SHOW_ROOM_TITLE + const canShowName = canShowAvatar && isGroup if (!message) return null diff --git a/packages/components/modules/messages/MessagesList/MessagesGroup/types.ts b/packages/components/modules/messages/MessagesList/MessagesGroup/types.ts index c35dba5e..35ebc4dd 100644 --- a/packages/components/modules/messages/MessagesList/MessagesGroup/types.ts +++ b/packages/components/modules/messages/MessagesList/MessagesGroup/types.ts @@ -1,16 +1,13 @@ import { FC } from 'react' -import { MessagesListFragment$data } from '../../../../__generated__/MessagesListFragment.graphql' import { MessageNode } from '../../types' import { MessageItemProps } from './MessageItem/types' -type Participants = NonNullable - export interface MessagesGroupProps { allMessages: MessageNode[] message: MessageNode messageIndex: number - roomParticipantsCount: Participants['totalCount'] + isGroup?: boolean allMessagesLastIndex: number hasNext: boolean MessageItem?: FC diff --git a/packages/components/modules/messages/MessagesList/index.tsx b/packages/components/modules/messages/MessagesList/index.tsx index ee1f74b7..76903907 100644 --- a/packages/components/modules/messages/MessagesList/index.tsx +++ b/packages/components/modules/messages/MessagesList/index.tsx @@ -70,7 +70,7 @@ const MessagesList: FC = ({ allMessages={allMessages} message={message} messageIndex={messageIndex} - roomParticipantsCount={room?.participants?.totalCount} + isGroup={room?.isGroup} allMessagesLastIndex={allMessagesLastIndex} hasNext={hasNext} {...MessagesGroupProps} @@ -82,7 +82,7 @@ const MessagesList: FC = ({ allMessagesLastIndex, firstItemIndex, hasNext, - room?.participants?.totalCount, + room?.isGroup, MessagesGroup, MessagesGroupProps, ], diff --git a/packages/components/modules/messages/constants.ts b/packages/components/modules/messages/constants.ts index 7974591b..51ad883b 100644 --- a/packages/components/modules/messages/constants.ts +++ b/packages/components/modules/messages/constants.ts @@ -1,3 +1 @@ -export const MINIMUM_AMOUNT_OF_PARTICIPANTS_TO_SHOW_ROOM_TITLE = 3 - export const MAXIMUM_DIFF_TO_GROUP_MESSAGES_CREATED_TIME = 3 // in minutes diff --git a/packages/components/modules/messages/graphql/mutations/CreateChatRoom.ts b/packages/components/modules/messages/graphql/mutations/CreateChatRoom.ts index 1809b800..1d735937 100644 --- a/packages/components/modules/messages/graphql/mutations/CreateChatRoom.ts +++ b/packages/components/modules/messages/graphql/mutations/CreateChatRoom.ts @@ -11,6 +11,11 @@ export const CreateChatRoomMutationQuery = graphql` room @prependEdge(connections: $connections) { node { id + isGroup + title + image(width: 100, height: 100) { + url + } participants { edges { node { diff --git a/packages/components/modules/messages/graphql/queries/ChatRoomHeaderFragment.ts b/packages/components/modules/messages/graphql/queries/ChatRoomHeaderFragment.ts index 7ebd5d41..e3ddacba 100644 --- a/packages/components/modules/messages/graphql/queries/ChatRoomHeaderFragment.ts +++ b/packages/components/modules/messages/graphql/queries/ChatRoomHeaderFragment.ts @@ -6,6 +6,7 @@ export const ChatRoomHeaderFragment = graphql` url } title + isGroup participants { edges { node { diff --git a/packages/components/modules/messages/graphql/queries/MessagesList.ts b/packages/components/modules/messages/graphql/queries/MessagesList.ts index 27c56c9c..fe1ac74c 100644 --- a/packages/components/modules/messages/graphql/queries/MessagesList.ts +++ b/packages/components/modules/messages/graphql/queries/MessagesList.ts @@ -8,6 +8,7 @@ export const MessagesListFragment = graphql` participants { totalCount } + isGroup unreadMessages { count markedUnread diff --git a/packages/components/modules/messages/index.ts b/packages/components/modules/messages/index.ts index de98be6e..ec65636e 100644 --- a/packages/components/modules/messages/index.ts +++ b/packages/components/modules/messages/index.ts @@ -13,6 +13,9 @@ export type * from './SendMessage/types' export { default as CreateChatRoomList } from './CreateChatRoomList' export type * from './CreateChatRoomList/types' +export { default as CreateGroup } from './CreateGroup' +export type * from './CreateGroup/types' + export * from './context' export * from './graphql/mutations/SendMessage' diff --git a/packages/components/modules/messages/types.ts b/packages/components/modules/messages/types.ts index 4a3c0807..0375596e 100644 --- a/packages/components/modules/messages/types.ts +++ b/packages/components/modules/messages/types.ts @@ -1,5 +1,11 @@ +import { AllProfilesListFragment$data } from '../../__generated__/AllProfilesListFragment.graphql' import { MessagesListFragment$data } from '../../__generated__/MessagesListFragment.graphql' export type AllMessages = NonNullable export type MessageEdges = AllMessages['edges'] export type MessageNode = NonNullable['node'] + +export type AllProfiles = NonNullable +export type AllProfilesEdges = AllProfiles['edges'] +export type ProfileEdge = AllProfilesEdges[number] +export type ProfileNode = NonNullable['node'] diff --git a/packages/components/modules/messages/utils.ts b/packages/components/modules/messages/utils.ts index f78a8261..5f428fd4 100644 --- a/packages/components/modules/messages/utils.ts +++ b/packages/components/modules/messages/utils.ts @@ -2,15 +2,12 @@ import { useCurrentProfile } from '@baseapp-frontend/authentication' import { ChatRoomHeaderFragment$data } from '../../__generated__/ChatRoomHeaderFragment.graphql' -// TODO Update this when a isGroupChat field is added on the BE -export const isGroupChat = (chatRoom: ChatRoomHeaderFragment$data) => chatRoom.title !== null - export const getParticipantCount = (chatRoom: ChatRoomHeaderFragment$data) => chatRoom.participants?.edges.length export const useNameAndAvatar = (roomHeader: ChatRoomHeaderFragment$data) => { const { currentProfile } = useCurrentProfile() - if (isGroupChat(roomHeader)) { + if (roomHeader.isGroup) { return { title: roomHeader.title, avatar: roomHeader.image?.url, diff --git a/packages/components/package.json b/packages/components/package.json index b5542f20..24ba1cf2 100644 --- a/packages/components/package.json +++ b/packages/components/package.json @@ -1,7 +1,7 @@ { "name": "@baseapp-frontend/components", "description": "BaseApp components modules such as comments, notifications, messages, and more.", - "version": "0.0.37", + "version": "0.0.38", "main": "./index.ts", "types": "dist/index.d.ts", "sideEffects": false, diff --git a/packages/components/schema.graphql b/packages/components/schema.graphql index d1c35e57..77bf3c24 100644 --- a/packages/components/schema.graphql +++ b/packages/components/schema.graphql @@ -103,10 +103,11 @@ type ChatRoom implements Node { image(width: Int!, height: Int!): File lastMessage: Message lastMessageTime: DateTime + isGroup: Boolean! participants(offset: Int, before: String, after: String, first: Int, last: Int): ChatRoomParticipantConnection pk: Int! allMessages(offset: Int, before: String, after: String, first: Int, last: Int, verb: Verbs): MessageConnection - unreadMessages(profileId: ID): UnreadMessages + unreadMessages(profileId: ID): UnreadMessageCount isArchived(profileId: ID): Boolean } @@ -138,6 +139,8 @@ type ChatRoomConnection { input ChatRoomCreateInput { profileId: ID! participants: [ID]! + isGroup: Boolean = false + title: String clientMutationId: String } @@ -644,8 +647,8 @@ type Mutation { chatRoomCreate(input: ChatRoomCreateInput!): ChatRoomCreatePayload chatRoomSendMessage(input: ChatRoomSendMessageInput!): ChatRoomSendMessagePayload chatRoomReadMessages(input: ChatRoomReadMessagesInput!): ChatRoomReadMessagesPayload - chatRoomArchive(input: ChatRoomArchiveInput!): ChatRoomArchivePayload chatRoomUnread(input: ChatRoomUnreadInput!): ChatRoomUnreadPayload + chatRoomArchive(input: ChatRoomArchiveInput!): ChatRoomArchivePayload reportCreate(input: ReportCreateInput!): ReportCreatePayload followToggle(input: FollowToggleInput!): FollowTogglePayload blockToggle(input: BlockToggleInput!): BlockTogglePayload @@ -1097,6 +1100,7 @@ type Profile implements Node & PermissionsInterface & PageInterface & FollowsInt orderBy: String ): ProfileUserRoleConnection chatroomparticipantSet(offset: Int, before: String, after: String, first: Int, last: Int, profile_TargetContentType: ID): ChatRoomParticipantConnection! + unreadmessagecountSet(offset: Int, before: String, after: String, first: Int, last: Int): UnreadMessageCountConnection! messageSet(offset: Int, before: String, after: String, first: Int, last: Int, verb: Verbs): MessageConnection! following(offset: Int, before: String, after: String, first: Int, last: Int, targetIsFollowingBack: Boolean): FollowConnection followers(offset: Int, before: String, after: String, first: Int, last: Int, targetIsFollowingBack: Boolean): FollowConnection @@ -1593,9 +1597,32 @@ type Subscription { onCommentChange(targetObjectId: ID): OnCommentChange } -type UnreadMessages { - count: Int - markedUnread: Boolean +type UnreadMessageCount implements Node { + markedUnread: Boolean! + count: Int! + + """The ID of the object""" + id: ID! + pk: Int! +} + +type UnreadMessageCountConnection { + """Pagination data for this connection.""" + pageInfo: PageInfo! + + """Contains the nodes in this connection.""" + edges: [UnreadMessageCountEdge]! + totalCount: Int + edgeCount: Int +} + +"""A Relay edge containing a `UnreadMessageCount` and its cursor.""" +type UnreadMessageCountEdge { + """The item at the end of the edge""" + node: UnreadMessageCount + + """A cursor for use in pagination""" + cursor: String! } type URLPath implements Node { diff --git a/packages/design-system/CHANGELOG.md b/packages/design-system/CHANGELOG.md index 0a30e6e8..17e64046 100644 --- a/packages/design-system/CHANGELOG.md +++ b/packages/design-system/CHANGELOG.md @@ -1,5 +1,11 @@ # @baseapp-frontend/design-system +## 0.0.26 + +### Patch Changes + +- Add `CircledAvatar` and `FileUploadButton` components. + ## 0.0.25 ### Patch Changes diff --git a/packages/design-system/components/avatars/CircledAvatar/index.tsx b/packages/design-system/components/avatars/CircledAvatar/index.tsx new file mode 100644 index 00000000..becc7c31 --- /dev/null +++ b/packages/design-system/components/avatars/CircledAvatar/index.tsx @@ -0,0 +1,26 @@ +import { FC } from 'react' + +import AvatarWithPlaceholder from '../AvatarWithPlaceholder' +import { AvatarContainer } from './styled' +import { CircledAvatarProps } from './types' + +const CircledAvatar: FC = ({ + width = 40, + height = 40, + hasError, + children, + ...props +}) => ( + + ({ border: `solid 8px ${palette.background.default}` })} + {...props} + > + {children} + + +) + +export default CircledAvatar diff --git a/packages/design-system/components/avatars/CircledAvatar/styled.tsx b/packages/design-system/components/avatars/CircledAvatar/styled.tsx new file mode 100644 index 00000000..3112fac0 --- /dev/null +++ b/packages/design-system/components/avatars/CircledAvatar/styled.tsx @@ -0,0 +1,18 @@ +import { Box } from '@mui/material' +import { alpha, styled } from '@mui/material/styles' + +import { AvatarContainerProps } from './types' + +export const AvatarContainer = styled(Box)( + ({ theme, width, height, hasError }) => ({ + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + width: width + 4, + height: height + 4, + background: hasError + ? alpha(theme.palette.error.main, 1) + : alpha(theme.palette.grey[400], 0.32), + borderRadius: '50%', + }), +) diff --git a/packages/design-system/components/avatars/CircledAvatar/types.ts b/packages/design-system/components/avatars/CircledAvatar/types.ts new file mode 100644 index 00000000..fb06d0b2 --- /dev/null +++ b/packages/design-system/components/avatars/CircledAvatar/types.ts @@ -0,0 +1,11 @@ +import { BoxProps } from '@mui/material' + +import { AvatarWithPlaceholderProps } from '../AvatarWithPlaceholder/types' + +export interface AvatarContainerProps extends BoxProps { + width: number + height: number + hasError: boolean +} + +export type CircledAvatarProps = AvatarContainerProps & AvatarWithPlaceholderProps diff --git a/packages/design-system/components/avatars/index.ts b/packages/design-system/components/avatars/index.ts index 0ff9578f..71b7fd28 100644 --- a/packages/design-system/components/avatars/index.ts +++ b/packages/design-system/components/avatars/index.ts @@ -3,3 +3,6 @@ export type * from './AvatarWithPlaceholder/types' export { default as ClickableAvatar } from './ClickableAvatar' export type * from './ClickableAvatar/types' + +export { default as CircledAvatar } from './CircledAvatar' +export type * from './CircledAvatar/types' diff --git a/packages/design-system/components/buttons/FileUploadButton/__storybook__/FileUploadButton.mdx b/packages/design-system/components/buttons/FileUploadButton/__storybook__/FileUploadButton.mdx new file mode 100644 index 00000000..2a2def54 --- /dev/null +++ b/packages/design-system/components/buttons/FileUploadButton/__storybook__/FileUploadButton.mdx @@ -0,0 +1,59 @@ +import { Meta } from '@storybook/addon-docs' + + + +# Component Documentation + +## FileUploadButton + +- **Purpose**: The `FileUploadButton` component provides a button for file upload functionality with validation and customization options. +- **Expected Behavior**: When clicked, it opens the file picker dialog, allowing users to select a file. The component validates file size based on the provided `maxSize` prop, displaying a toast notification if the file is too large. + +## Use Cases + +- **Current Usage**: This component is currently used in the `AccountProfile` component for profile picture uploads. +- **Potential Usage**: The `FileUploadButton` could also be used in other forms where users need to upload files, such as document upload sections, avatars in user profiles, and attachment uploads in messaging features. + +## Props + +- **label** (string): The text displayed on the button. Default is `'Upload File'`. +- **name** (string): The name associated with the file input, used by `react-hook-form` for form control. +- **control** (object): The form control object from `react-hook-form`, necessary for form validation and submission handling. +- **setFile** (function): Callback function to set the uploaded file in the form state, provided by the parent component. +- **accept** (string, optional): Specifies the accepted file types (e.g., `'.jpg, .png, .pdf'`). +- **maxSize** (number, optional): Maximum file size in bytes. If the file exceeds this size, a toast notification will indicate the error. + +## Notes + +- **Related Components**: uConsider using this component alongside `FilePreview` or `FileList` components if displaying the uploaded files is necessary. + +## Example Usage + +```javascript +import { useForm } from 'react-hook-form' + +import FileUploadButton from '../FileUploadButton' + +const MyComponent = () => { + const { control, setValue } = useForm() + + const setFile = (name, file) => { + setValue(name, file, { + shouldValidate: false, + shouldDirty: true, + shouldTouch: true, + }) + } + + return ( + + ) +} +``` diff --git a/packages/design-system/components/buttons/FileUploadButton/__storybook__/stories.tsx b/packages/design-system/components/buttons/FileUploadButton/__storybook__/stories.tsx new file mode 100644 index 00000000..2efbc603 --- /dev/null +++ b/packages/design-system/components/buttons/FileUploadButton/__storybook__/stories.tsx @@ -0,0 +1,34 @@ +import React from 'react' + +import { Meta, StoryFn } from '@storybook/react' +import { useForm } from 'react-hook-form' + +import FileUploadButton from '..' +import { FileUploadButtonProps } from '../types' + +const meta: Meta = { + title: '@baseapp-frontend | designSystem/Buttons/FileUploadButton', + component: FileUploadButton, +} + +export default meta + +const Template: StoryFn = (args) => { + const { control, setValue } = useForm() + + const setFile = (name: string, file: File) => { + setValue(name, file, { + shouldValidate: false, + shouldDirty: true, + shouldTouch: true, + }) + } + + return +} + +export const Default: StoryFn = Template.bind({}) +Default.args = { + label: 'Upload File', + name: 'file', +} diff --git a/packages/design-system/components/buttons/FileUploadButton/index.tsx b/packages/design-system/components/buttons/FileUploadButton/index.tsx new file mode 100644 index 00000000..3e6ea4f1 --- /dev/null +++ b/packages/design-system/components/buttons/FileUploadButton/index.tsx @@ -0,0 +1,62 @@ +import { ChangeEventHandler, FC, useRef } from 'react' + +import { useNotification } from '@baseapp-frontend/utils' + +import { Button, Input } from '@mui/material' +import { Controller } from 'react-hook-form' + +import { FileUploadButtonProps } from './types' + +const FileUploadButton: FC = (props) => { + const fileRef = useRef() + const { sendToast } = useNotification() + const { accept, maxSize, ...fileUploadProps } = props + const { control, label, name, setFile } = fileUploadProps + return ( + <> + + { + const handleOnChange: ChangeEventHandler = ( + event, + ) => { + const { files } = event.target as HTMLInputElement + if (files![0] && maxSize && files![0].size > maxSize) { + sendToast(`This file is too large (max ${maxSize / 1024 / 1024}MB).`, { + type: 'error', + }) + } else { + field.onChange(files![0]) + setFile(name, files![0], { + shouldValidate: false, + shouldDirty: true, + shouldTouch: true, + }) + } + } + return ( + + ) + }} + /> + + ) +} + +export default FileUploadButton diff --git a/packages/design-system/components/buttons/FileUploadButton/types.ts b/packages/design-system/components/buttons/FileUploadButton/types.ts new file mode 100644 index 00000000..6476254a --- /dev/null +++ b/packages/design-system/components/buttons/FileUploadButton/types.ts @@ -0,0 +1,24 @@ +import { RequireAllOrNone } from '@baseapp-frontend/utils' + +import { ButtonProps } from '@mui/material' +import { Control, FieldValues, UseFormSetValue } from 'react-hook-form' + +type ControlProps = RequireAllOrNone<{ + control?: Control +}> + +type AvatarProps = { + name: string + // TODO type this better + setFile: UseFormSetValue + label: string +} + +type FileInputProps = { + accept?: string + maxSize?: number +} + +type CustomButtonProps = Exclude + +export type FileUploadButtonProps = CustomButtonProps & ControlProps & AvatarProps & FileInputProps diff --git a/packages/design-system/components/buttons/index.ts b/packages/design-system/components/buttons/index.ts index dc3700f1..8699dcfd 100644 --- a/packages/design-system/components/buttons/index.ts +++ b/packages/design-system/components/buttons/index.ts @@ -1,2 +1,5 @@ export { default as IconButton } from './IconButton' export type * from './IconButton/types' + +export { default as FileUploadButton } from './FileUploadButton' +export type * from './FileUploadButton/types' diff --git a/packages/design-system/package.json b/packages/design-system/package.json index 746a206d..99fc94d6 100644 --- a/packages/design-system/package.json +++ b/packages/design-system/package.json @@ -1,7 +1,7 @@ { "name": "@baseapp-frontend/design-system", "description": "Design System components and configurations.", - "version": "0.0.25", + "version": "0.0.26", "main": "./index.ts", "types": "dist/index.d.ts", "sideEffects": false, diff --git a/packages/wagtail/CHANGELOG.md b/packages/wagtail/CHANGELOG.md index 32bc6a19..5a8b1d18 100644 --- a/packages/wagtail/CHANGELOG.md +++ b/packages/wagtail/CHANGELOG.md @@ -1,5 +1,12 @@ # @baseapp-frontend/wagtail +## 1.0.11 + +### Patch Changes + +- Updated dependencies + - @baseapp-frontend/design-system@0.0.26 + ## 1.0.10 ### Patch Changes diff --git a/packages/wagtail/package.json b/packages/wagtail/package.json index 42679ead..7c2ef1ab 100644 --- a/packages/wagtail/package.json +++ b/packages/wagtail/package.json @@ -1,7 +1,7 @@ { "name": "@baseapp-frontend/wagtail", "description": "BaseApp Wagtail", - "version": "1.0.10", + "version": "1.0.11", "main": "./index.ts", "types": "dist/index.d.ts", "sideEffects": false,