-
Notifications
You must be signed in to change notification settings - Fork 9
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
a479772
commit a2e767a
Showing
11 changed files
with
814 additions
and
29 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
import { SocialCallDataArgs, socialCallName } from '@subsocial/data-hub-sdk' | ||
import axios from 'axios' | ||
import { createSocialDataEventPayload, DatahubParams } from './utils' | ||
|
||
export async function createSuperLike( | ||
params: DatahubParams<SocialCallDataArgs<'synth_active_staking_create_super_like'>>, | ||
) { | ||
const input = createSocialDataEventPayload( | ||
socialCallName.synth_active_staking_create_super_like, | ||
params, | ||
) | ||
|
||
const res = await axios.post('/api/datahub/super-likes', input) | ||
return res.data | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
import { | ||
SocialCallDataArgs, | ||
socialCallName, | ||
SocialEventDataApiInput, | ||
SocialEventDataType, | ||
socialEventProtVersion, | ||
} from '@subsocial/data-hub-sdk' | ||
|
||
export type DatahubParams<T> = { | ||
address: string | ||
|
||
uuid?: string | ||
timestamp?: number | ||
|
||
args: T | ||
} | ||
|
||
export function createSocialDataEventPayload<T extends keyof typeof socialCallName>( | ||
callName: T, | ||
{ timestamp, address, uuid, args }: DatahubParams<SocialCallDataArgs<T>>, | ||
) { | ||
const payload: SocialEventDataApiInput = { | ||
protVersion: socialEventProtVersion['0.1'], | ||
dataType: SocialEventDataType.offChain, | ||
callData: { | ||
name: callName, | ||
signer: address || '', | ||
args: JSON.stringify(args), | ||
timestamp: timestamp || Date.now(), | ||
uuid: uuid || crypto.randomUUID(), | ||
}, | ||
providerAddr: address, | ||
sig: '', | ||
} | ||
return payload | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
import { PostStruct } from '@subsocial/api/types' | ||
import { Button, ButtonProps } from 'antd' | ||
import clsx from 'clsx' | ||
import { CSSProperties } from 'react' | ||
import { AiFillHeart, AiOutlineHeart } from 'react-icons/ai' | ||
import { useOpenCloseOnBoardingModal } from 'src/rtk/features/onBoarding/onBoardingHooks' | ||
import { useAuth } from '../auth/AuthContext' | ||
import { useMyAddress } from '../auth/MyAccountsContext' | ||
import { IconWithLabel } from '../utils' | ||
import { createSuperLike } from '../utils/datahub-queue/super-likes' | ||
|
||
export type SuperLikeProps = ButtonProps & { | ||
post: PostStruct | ||
} | ||
|
||
export default function SuperLike({ post, ...props }: SuperLikeProps) { | ||
const isActive = true | ||
const count = 21 | ||
const openOnBoardingModal = useOpenCloseOnBoardingModal() | ||
const myAddress = useMyAddress() | ||
|
||
const { | ||
openSignInModal, | ||
state: { | ||
completedSteps: { hasTokens }, | ||
}, | ||
} = useAuth() | ||
|
||
const onClick = async () => { | ||
if (!myAddress) { | ||
openSignInModal() | ||
return | ||
} | ||
if (!hasTokens) { | ||
openOnBoardingModal('open', { type: 'partial', toStep: 'energy' }) | ||
return | ||
} | ||
|
||
await createSuperLike({ address: myAddress, args: { postId: post.id } }) | ||
} | ||
|
||
const likeStyle: CSSProperties = { position: 'relative', top: '0.07em' } | ||
const icon = isActive ? ( | ||
<AiFillHeart className='FontSemilarge ColorPrimary' style={likeStyle} /> | ||
) : ( | ||
<AiOutlineHeart className='FontSemilarge' style={likeStyle} /> | ||
) | ||
return ( | ||
<Button | ||
className={clsx('DfVoterButton ColorMuted', isActive && 'ColorPrimary', props.className)} | ||
onClick={onClick} | ||
> | ||
<IconWithLabel icon={icon} count={count} /> | ||
</Button> | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
import { SocialEventDataApiInput } from '@subsocial/data-hub-sdk' | ||
import { NextApiRequest, NextApiResponse } from 'next' | ||
import { ApiResponse, handlerWrapper } from 'src/server/common' | ||
import { createSuperLikeServer } from 'src/server/datahub-queue/super-likes' | ||
import { z } from 'zod' | ||
|
||
export default function handler(req: NextApiRequest, res: NextApiResponse) { | ||
if (req.method === 'POST') { | ||
return POST_handler(req, res) | ||
} | ||
|
||
return res.status(405).send('Method Not Allowed') | ||
} | ||
|
||
export type ApiDatahubModerationBody = SocialEventDataApiInput | ||
export type ApiDatahubModerationResponse = ApiResponse | ||
const POST_handler = handlerWrapper({ | ||
inputSchema: z.any(), | ||
dataGetter: req => req.body, | ||
})({ | ||
allowedMethods: ['POST'], | ||
errorLabel: 'super-likes', | ||
handler: async (data: ApiDatahubModerationBody, _req, res) => { | ||
await createSuperLikeServer(data) | ||
|
||
res.json({ | ||
message: 'OK', | ||
success: true, | ||
}) | ||
}, | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
import { Keyring } from '@polkadot/keyring' | ||
import { waitReady } from '@polkadot/wasm-crypto' | ||
import { NextApiRequest, NextApiResponse } from 'next' | ||
import { serverMnemonic } from 'src/config/env' | ||
import { z } from 'zod' | ||
|
||
export type ApiResponse<T = {}> = T & { | ||
success: boolean | ||
message: string | ||
errors?: any | ||
} | ||
|
||
export function handlerWrapper<Input extends z.ZodTypeAny>(config: { | ||
inputSchema: Input | null | ||
dataGetter: (req: NextApiRequest) => unknown | ||
}) { | ||
return <Output>(handlerConfig: { | ||
errorLabel?: string | ||
handler: ( | ||
data: z.infer<Input>, | ||
req: NextApiRequest, | ||
res: NextApiResponse<ApiResponse<Output>>, | ||
) => PromiseLike<void> | ||
allowedMethods?: ('GET' | 'POST' | 'PUT' | 'DELETE')[] | ||
}) => { | ||
return async (req: NextApiRequest, res: NextApiResponse<ApiResponse<Output>>) => { | ||
const { handler, allowedMethods, errorLabel } = handlerConfig | ||
|
||
if (!allowedMethods?.includes(req.method ?? ('' as any))) return res.status(404).end() | ||
|
||
const { dataGetter, inputSchema } = config | ||
if (!inputSchema) { | ||
try { | ||
return await handler(null, req, res) | ||
} catch (err) { | ||
console.error(`Error in ${errorLabel || 'handler'}:`, err) | ||
return res.status(500).send({ | ||
success: false, | ||
message: 'Internal server error', | ||
errors: err, | ||
} as ApiResponse<Output>) | ||
} | ||
} | ||
|
||
const params = inputSchema.safeParse(dataGetter(req)) | ||
|
||
if (!params.success) { | ||
return res.status(400).send({ | ||
success: false, | ||
message: 'Invalid request body', | ||
errors: params.error.errors, | ||
} as ApiResponse<Output>) | ||
} | ||
|
||
try { | ||
return await handler(params.data, req, res) | ||
} catch (err) { | ||
console.error(`Error in ${errorLabel || 'handler'}:`, err) | ||
return res.status(500).send({ | ||
success: false, | ||
message: 'Internal server error', | ||
errors: err, | ||
} as ApiResponse<Output>) | ||
} | ||
} | ||
} | ||
} | ||
|
||
export function getCommonErrorMessage(e: any, fallbackMsg = 'Error has been occurred') { | ||
return e ? { message: fallbackMsg, ...e }.message : fallbackMsg | ||
} | ||
|
||
export function convertNonce(nonce: number) { | ||
const newNonce = new Uint8Array(24) | ||
|
||
newNonce[0] = nonce | ||
|
||
return newNonce | ||
} | ||
|
||
export async function getServerAccount() { | ||
if (!serverMnemonic) throw new Error('Server Mnemonic is not set') | ||
const keyring = new Keyring() | ||
await waitReady() | ||
return keyring.addFromMnemonic(serverMnemonic, {}, 'sr25519') | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
import { SocialEventDataApiInput } from '@subsocial/data-hub-sdk' | ||
import { gql } from 'graphql-request' | ||
import { backendSigWrapper, datahubQueueRequest } from './utils' | ||
|
||
const CREATE_SUPER_LIKE = gql` | ||
mutation CreateSuperLike($createSuperLikeInput: CreateMutateActiveStakingSuperLikeInput!) { | ||
activeStakingCreateSuperLike(args: $createSuperLikeInput) { | ||
processed | ||
message | ||
} | ||
} | ||
` | ||
|
||
export async function createSuperLikeServer(input: SocialEventDataApiInput) { | ||
const signedPayload = await backendSigWrapper(input) | ||
await datahubQueueRequest({ | ||
document: CREATE_SUPER_LIKE, | ||
variables: { | ||
createSuperLikeInput: signedPayload, | ||
}, | ||
}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
import { KeyringPair } from '@polkadot/keyring/types' | ||
import { u8aToHex } from '@polkadot/util' | ||
import { SocialEventDataApiInput } from '@subsocial/data-hub-sdk' | ||
import { GraphQLClient, RequestOptions, Variables } from 'graphql-request' | ||
import sortKeysRecursive from 'sort-keys-recursive' | ||
import { datahubQueueConfig } from 'src/config/env' | ||
import { getServerAccount } from '../common' | ||
|
||
export function datahubQueueRequest<T, V extends Variables = Variables>( | ||
config: RequestOptions<V, T>, | ||
) { | ||
const { url, token } = datahubQueueConfig || {} | ||
if (!url) throw new Error('Datahub (Queue) config is not set') | ||
|
||
const TIMEOUT = 3 * 1000 // 3 seconds | ||
const client = new GraphQLClient(url, { | ||
timeout: TIMEOUT, | ||
headers: { | ||
Authorization: `Bearer ${token}`, | ||
}, | ||
...config, | ||
}) | ||
|
||
return client.request({ url, ...config }) | ||
} | ||
|
||
export function datahubMutationWrapper<T extends (...args: any[]) => Promise<any>>(func: T) { | ||
return async (...args: Parameters<T>) => { | ||
if (!datahubQueueConfig.url || !datahubQueueConfig.token) return | ||
await func(...args) | ||
} | ||
} | ||
|
||
function signDatahubPayload(signer: KeyringPair | null, payload: { sig: string }) { | ||
if (!signer) throw new Error('Signer is not defined') | ||
const sortedPayload = sortKeysRecursive(payload) | ||
const sig = signer.sign(JSON.stringify(sortedPayload)) | ||
const hexSig = u8aToHex(sig) | ||
payload.sig = hexSig | ||
} | ||
export const backendSigWrapper = async (input: SocialEventDataApiInput) => { | ||
const signer = await getServerAccount() | ||
if (!signer) throw new Error('Invalid Mnemonic') | ||
|
||
input.providerAddr = signer.address | ||
signDatahubPayload(signer, input) | ||
|
||
return input | ||
} |
Oops, something went wrong.