From c3955ec939488afe8e4ea7d51b1eeff4c8a6cee7 Mon Sep 17 00:00:00 2001 From: teodorus-nathaniel Date: Fri, 14 Jun 2024 20:50:44 +0700 Subject: [PATCH] Use server time for sending message --- src/components/chats/ChatForm.tsx | 25 ++++++++++++++----- src/pages/api/time.ts | 15 +++++++++++ src/services/api/query.ts | 6 +++++ .../datahub/content-staking/mutation.ts | 2 +- src/services/datahub/identity/mutation.ts | 2 +- src/services/datahub/moderation/mutation.ts | 6 ++++- src/services/datahub/posts/mutation.ts | 16 +++++++++--- src/services/datahub/referral/mutation.ts | 2 +- src/services/datahub/utils.ts | 10 +++++--- src/services/subsocial/commentIds/mutation.ts | 13 +++++++--- src/services/subsocial/posts/mutation.ts | 9 ++++--- 11 files changed, 80 insertions(+), 26 deletions(-) create mode 100644 src/pages/api/time.ts diff --git a/src/components/chats/ChatForm.tsx b/src/components/chats/ChatForm.tsx index 2919db3f8..c3afbd63a 100644 --- a/src/components/chats/ChatForm.tsx +++ b/src/components/chats/ChatForm.tsx @@ -8,7 +8,7 @@ import useLoginOption from '@/hooks/useLoginOption' import useRequestTokenAndSendMessage from '@/hooks/useRequestTokenAndSendMessage' import { showErrorToast } from '@/hooks/useToastError' import { useConfigContext } from '@/providers/config/ConfigProvider' -import { getPostQuery } from '@/services/api/query' +import { getPostQuery, getServerTime } from '@/services/api/query' import { apiInstance } from '@/services/api/utils' import { useSendOffchainMessage } from '@/services/datahub/posts/mutation' import { @@ -237,11 +237,24 @@ export default function ChatForm({ const isOffchainPosting = env.NEXT_PUBLIC_OFFCHAIN_POSTING_HUBS.includes(hubId) if (isOffchainPosting) { - sendOffchainMessage({ - ...messageParams, - uuid: crypto.randomUUID(), - timestamp: Date.now(), - }) + try { + const serverTime = await getServerTime() + sendOffchainMessage({ + ...messageParams, + uuid: crypto.randomUUID(), + timestamp: serverTime, + }) + } catch (err) { + showErrorSendingMessageToast( + err, + 'Failed to get server time', + messageParams, + { + reloadUnsentMessage, + setIsDisabledInput, + } + ) + } } else if (shouldSendMessage) { sendMessage(messageParams) } else { diff --git a/src/pages/api/time.ts b/src/pages/api/time.ts new file mode 100644 index 000000000..5ba1fe90b --- /dev/null +++ b/src/pages/api/time.ts @@ -0,0 +1,15 @@ +import { ApiResponse, handlerWrapper } from '@/server/common' +import { z } from 'zod' + +export type ApiTimeResponse = ApiResponse<{ time: number }> + +export default handlerWrapper({ + inputSchema: z.any(), + dataGetter: (req) => req.query, +})({ + errorLabel: 'time', + allowedMethods: ['GET'], + handler: async (_, __, res) => { + res.json({ success: true, message: 'OK', time: Date.now() }) + }, +}) diff --git a/src/services/api/query.ts b/src/services/api/query.ts index 2a4b13bd9..3c1b27a72 100644 --- a/src/services/api/query.ts +++ b/src/services/api/query.ts @@ -5,6 +5,7 @@ import { import { Identities } from '@/pages/api/identities' import { ApiNftParams, ApiNftResponse } from '@/pages/api/nft' import { ApiStakedParams, ApiStakedResponse } from '@/pages/api/staked' +import { ApiTimeResponse } from '@/pages/api/time' import { createQuery, poolQuery } from '@/subsocial-query' import { PostData } from '@subsocial/api/types' import { useMemo } from 'react' @@ -165,3 +166,8 @@ export const getHasUserStakedQuery = createQuery({ enabled: !!data?.address, }), }) + +export async function getServerTime() { + const res = await apiInstance.get('/api/time') + return (res.data as ApiTimeResponse).time +} diff --git a/src/services/datahub/content-staking/mutation.ts b/src/services/datahub/content-staking/mutation.ts index 2864dfeda..054608e63 100644 --- a/src/services/datahub/content-staking/mutation.ts +++ b/src/services/datahub/content-staking/mutation.ts @@ -12,7 +12,7 @@ import { getAddressLikeCountToPostQuery, getSuperLikeCountQuery } from './query' type CreateSuperLikeArgs = SocialCallDataArgs<'synth_active_staking_create_super_like'> async function createSuperLike(params: DatahubParams) { - const input = createSocialDataEventPayload( + const input = await createSocialDataEventPayload( socialCallName.synth_active_staking_create_super_like, params, params.args diff --git a/src/services/datahub/identity/mutation.ts b/src/services/datahub/identity/mutation.ts index 70731e271..a0654054e 100644 --- a/src/services/datahub/identity/mutation.ts +++ b/src/services/datahub/identity/mutation.ts @@ -23,7 +23,7 @@ export async function linkIdentity( id, provider, } - const input = createSocialDataEventPayload( + const input = await createSocialDataEventPayload( socialCallName.synth_create_linked_identity, params, eventArgs diff --git a/src/services/datahub/moderation/mutation.ts b/src/services/datahub/moderation/mutation.ts index 541b33fb6..90e97a803 100644 --- a/src/services/datahub/moderation/mutation.ts +++ b/src/services/datahub/moderation/mutation.ts @@ -26,7 +26,11 @@ async function moderationActions( ) { if (!data) return null - const input = createSignedSocialDataEvent(data.callName, data, data.args) + const input = await createSignedSocialDataEvent( + data.callName, + data, + data.args + ) const actionRes = await apiInstance.post< any, AxiosResponse, diff --git a/src/services/datahub/posts/mutation.ts b/src/services/datahub/posts/mutation.ts index 36a398284..bdc2c3c90 100644 --- a/src/services/datahub/posts/mutation.ts +++ b/src/services/datahub/posts/mutation.ts @@ -76,7 +76,7 @@ async function createPostData( ipfsSrc: cid, } - const input = createSignedSocialDataEvent( + const input = await createSignedSocialDataEvent( socialCallName.create_post, params, eventArgs, @@ -112,7 +112,7 @@ async function updatePostData( postId, ipfsSrc: content?.cid ?? null, } - const input = createSignedSocialDataEvent( + const input = await createSignedSocialDataEvent( socialCallName.update_post, params, eventArgs, @@ -165,7 +165,11 @@ async function notifyCreatePostFailedOrRetryStatus( } } - const input = createSignedSocialDataEvent(event.name, params, event.args) + const input = await createSignedSocialDataEvent( + event.name, + params, + event.args + ) await apiInstance.post( '/api/datahub/post', @@ -217,7 +221,11 @@ async function notifyUpdatePostFailedOrRetryStatus( } } - const input = createSignedSocialDataEvent(event.name, params, event.args) + const input = await createSignedSocialDataEvent( + event.name, + params, + event.args + ) await apiInstance.post( '/api/datahub/post', diff --git a/src/services/datahub/referral/mutation.ts b/src/services/datahub/referral/mutation.ts index 1da3559e1..bdfe4c289 100644 --- a/src/services/datahub/referral/mutation.ts +++ b/src/services/datahub/referral/mutation.ts @@ -12,7 +12,7 @@ import { DatahubParams, createSocialDataEventPayload } from '../utils' type SetReferrerIdArgs = SocialCallDataArgs<'synth_social_profile_add_referrer_id'> async function setReferrerId(params: DatahubParams) { - const input = createSocialDataEventPayload( + const input = await createSocialDataEventPayload( socialCallName.synth_social_profile_add_referrer_id, params, params.args diff --git a/src/services/datahub/utils.ts b/src/services/datahub/utils.ts index c95c95eb9..32e32be4d 100644 --- a/src/services/datahub/utils.ts +++ b/src/services/datahub/utils.ts @@ -17,6 +17,7 @@ import { GraphQLClient, RequestOptions, Variables } from 'graphql-request' import { Client, createClient } from 'graphql-ws' import ws from 'isomorphic-ws' import sortKeysRecursive from 'sort-keys-recursive' +import { getServerTime } from '../api/query' dayjs.extend(utc) dayjs.extend(isoWeek) @@ -91,7 +92,7 @@ export function signDatahubPayload( payload.sig = hexSig } -export function createSocialDataEventPayload< +export async function createSocialDataEventPayload< T extends keyof typeof socialCallName >( callName: T, @@ -109,6 +110,7 @@ export function createSocialDataEventPayload< content?: PostContent ) { const owner = proxyToAddress || address + const serverTime = await getServerTime() const payload: SocialEventDataApiInput = { protVersion: socialEventProtVersion['0.1'], dataType: isOffchain @@ -118,7 +120,7 @@ export function createSocialDataEventPayload< name: callName, signer: owner || '', args: JSON.stringify(eventArgs), - timestamp: timestamp || Date.now(), + timestamp: timestamp || serverTime, uuid: uuid || crypto.randomUUID(), proxy: proxyToAddress ? address : undefined, }, @@ -129,7 +131,7 @@ export function createSocialDataEventPayload< return payload } -export function createSignedSocialDataEvent< +export async function createSignedSocialDataEvent< T extends keyof typeof socialCallName >( callName: T, @@ -137,7 +139,7 @@ export function createSignedSocialDataEvent< eventArgs: SocialCallDataArgs, content?: PostContent ) { - const payload = createSocialDataEventPayload( + const payload = await createSocialDataEventPayload( callName, params, eventArgs, diff --git a/src/services/subsocial/commentIds/mutation.ts b/src/services/subsocial/commentIds/mutation.ts index bb8ee1a38..2c0de1826 100644 --- a/src/services/subsocial/commentIds/mutation.ts +++ b/src/services/subsocial/commentIds/mutation.ts @@ -1,7 +1,7 @@ import { getMaxMessageLength } from '@/constants/chat' import useWaitHasEnergy from '@/hooks/useWaitHasEnergy' import { useRevalidateChatPage, useSaveFile } from '@/services/api/mutation' -import { getPostQuery } from '@/services/api/query' +import { getPostQuery, getServerTime } from '@/services/api/query' import { isPersistentId } from '@/services/datahub/posts/fetcher' import datahubMutation from '@/services/datahub/posts/mutation' import { isDatahubAvailable } from '@/services/datahub/utils' @@ -163,7 +163,11 @@ export function useSendMessage(config?: MutationConfig) { } }, onSend: allowWindowUnload, - onError: ({ data, context, address }, error, isAfterTxGenerated) => { + onError: async ( + { data, context, address }, + error, + isAfterTxGenerated + ) => { allowWindowUnload() const content = context.content const optimisticId = content.optimisticId @@ -182,6 +186,7 @@ export function useSendMessage(config?: MutationConfig) { getPostQuery.invalidate(client, data.messageIdToEdit) } } else { + const serverTime = await getServerTime() if (isCreating) { datahubMutation.notifyCreatePostFailedOrRetryStatus({ ...getCurrentWallet(), @@ -189,7 +194,7 @@ export function useSendMessage(config?: MutationConfig) { optimisticId, reason: error, }, - timestamp: Date.now(), + timestamp: serverTime, }) } else if (isUpdating) { datahubMutation.notifyUpdatePostFailedOrRetryStatus({ @@ -198,7 +203,7 @@ export function useSendMessage(config?: MutationConfig) { postId: messageIdToEdit, reason: error, }, - timestamp: Date.now(), + timestamp: serverTime, }) } } diff --git a/src/services/subsocial/posts/mutation.ts b/src/services/subsocial/posts/mutation.ts index cb4711111..d95ab9a1f 100644 --- a/src/services/subsocial/posts/mutation.ts +++ b/src/services/subsocial/posts/mutation.ts @@ -4,7 +4,7 @@ import { saveFile, useRevalidateChatPage, } from '@/services/api/mutation' -import { getPostQuery } from '@/services/api/query' +import { getPostQuery, getServerTime } from '@/services/api/query' import { isPersistentId } from '@/services/datahub/posts/fetcher' import datahubMutation from '@/services/datahub/posts/mutation' import { isDatahubAvailable } from '@/services/datahub/utils' @@ -270,14 +270,15 @@ export function useUpsertPost( }) } }, - onError: ({ data, context }, error, isAfterTxGenerated) => { + onError: async ({ data, context }, error, isAfterTxGenerated) => { const { action, payload } = checkAction(data) if (!isAfterTxGenerated) return + const serverTime = await getServerTime() if (action === 'create' && context.content.optimisticId) { datahubMutation.notifyCreatePostFailedOrRetryStatus({ ...getCurrentWallet(), - timestamp: Date.now(), + timestamp: serverTime, args: { optimisticId: context.content.optimisticId, reason: error, @@ -290,7 +291,7 @@ export function useUpsertPost( postId: payload.postId, reason: error, }, - timestamp: Date.now(), + timestamp: serverTime, }) } },