Stylianos Gakis
11/15/2023, 9:03 AMtype Query {
chat(): Chat
}
type Chat {
id: ID!
messages: [ChatMessage!]!
...
}
interface ChatMessage {
id: ID!
...
}
Then on a mutation response, the message is also reported back, to get back the result quickly, so that there’s no delay before the next poll we do (we don’t use subscriptions here) and when it appears on the screen.
type Mutation {
chatSendText(input: ChatMessageTextInput!): ChatSendOutcome!
}
type ChatSendOutcome {
"""
The message to manually patch in the cache at the end of the messages list for immediate user feedback
"""
message: ChatMessage
}
I was thinking of using the gql cache as the source of truth for the chat messages, so I’ll just be `.watch()`ing the cache to reflect that in the UI, so I wanted to see if there’s a way to get the ChatMessage returned from this mutation to the right place in the cache too.
They do have a unique ID, and I will add on the extra.graphql file:
extend type Chat @typePolicy(keyFields: "id")
extend type ChatMessage @typePolicy(keyFields: "id")
but as I understood, the cache will need to put this specifically under the chat
query, and not globally for me to be able to watch for it.
Does that mean that if I want this to happen, I will need to simply write this response to the cache under the chat
query myself? Pretty much as I was doing here before?Stylianos Gakis
11/15/2023, 9:06 AMmbonnin
11/15/2023, 9:21 AMStylianos Gakis
11/15/2023, 9:23 AMmbonnin
11/15/2023, 9:27 AMloadRecords
/`mergeRecords` . Something like so (wildly untested so don't take it as gospel but should give the general idea)
val mutationResponse = // execute your mutation
val data = apolloClient.apolloStore.readOperation(ChatQuery(), customScalarAdapters)
val modifiedData = data.copy(data.chat.copy(messages = data.chat.messages + mutationResponse.data.chatSendText.message)
apolloClient.apolloStore.writeOperation(ChatQuery(), modifiedData, customScalarAdapters, CacheHeaders.NONE, true)
Stylianos Gakis
11/15/2023, 9:37 AMStylianos Gakis
11/16/2023, 12:51 AMprivate suspend fun populateCacheWithNewMessage(message: ChatMessagesQuery.Data.Chat.Message) {
val data = apolloClient.apolloStore.readOperation(ChatMessagesQuery(null), apolloClient.customScalarAdapters)
val modifiedData = data.copy(data.chat.copy(messages = data.chat.messages + message))
apolloClient.apolloStore.writeOperation(ChatMessagesQuery(null), modifiedData, apolloClient.customScalarAdapters, CacheHeaders.NONE, true)
}
With the only “tricky” part being that I had to do the mapping from the type generated for the mutation to the one generated for the query response, so I am calling it like
populateCacheWithNewMessage(
ChatMessagesQuery.Data.Chat.ChatMessageTextMessage(
__typename = "ChatMessageText",
id = chatMessage.id,
sender = when (chatMessage.sender) {
ChatMessage.Sender.MEMBER -> ChatMessageSender.MEMBER
ChatMessage.Sender.HEDVIG -> ChatMessageSender.HEDVIG
},
sentAt = chatMessage.sentAt,
text = (chatMessage as ChatMessage.ChatMessageText).text,
),
)
The only part which I do not love is that I have to write the __typename
myself, which I for now got by looking at what it was already from the okhttp logs, but this would break if that changed in any way, right? Should I be worried about that? Is there some way to get the proper __typename
in a type-safe way instead?
But so cool that I can just do this. Now only to figure out how to make sure that my “message is pending” and “message from cache” do not show on the UI at the same time. Oh boy I am pretty sure writing a chat is potentially an bottomless pit of things to take care of if I am not careful to gloss over some details 😄mbonnin
11/16/2023, 9:21 AM__typename
, I agree this is not ideal. We could make it a default value for the constructor but that doesn't work for interfaces so it becomes somewhat inconsistent. You should be able to use schema.packagename.type.ChatMessageText.type
to get the value in a more typesafe way.Stylianos Gakis
11/16/2023, 4:05 PMtype
, yeah I think I can do __typename = octopus.type.ChatMessageText.type.name,
here and it looks correct, thanks 😊