diff --git a/apps/mochi-web/components/settings/general/DefaultMessage.tsx b/apps/mochi-web/components/settings/general/DefaultMessage.tsx index 1070c4f37..8de5af143 100644 --- a/apps/mochi-web/components/settings/general/DefaultMessage.tsx +++ b/apps/mochi-web/components/settings/general/DefaultMessage.tsx @@ -7,34 +7,25 @@ import { } from '@mochi-ui/core' import { EditLine, TrashBinLine } from '@mochi-ui/icons' import React from 'react' -import { - Control, - Controller, - UseFormWatch, - useFieldArray, -} from 'react-hook-form' +import { Controller, useFieldArray, useFormContext } from 'react-hook-form' import { actionList } from '~constants/settings' -import { GeneralFormValue } from './types' +import { ResponseGeneralSettingData } from '~types/mochi-schema' import { MessageModal } from './MessageModal' -interface Props { - control: Control - watch: UseFormWatch -} - -export const DefaultMessage = ({ control, watch }: Props) => { +export const DefaultMessage = () => { + const { control, watch } = useFormContext() const { fields, append, remove, update } = useFieldArray({ control, - name: 'defaultMessage', + name: 'payment.default_message_settings', }) - const enableDefaultMessage = watch('enableDefaultMessage') + const enableDefaultMessage = watch('payment.default_message_enable') return (
Default message ( { {(enableDefaultMessage ? fields : []).map((each, index) => ( (
diff --git a/apps/mochi-web/components/settings/general/GeneralPage.tsx b/apps/mochi-web/components/settings/general/GeneralPage.tsx index 4cbfe713b..85904cdf0 100644 --- a/apps/mochi-web/components/settings/general/GeneralPage.tsx +++ b/apps/mochi-web/components/settings/general/GeneralPage.tsx @@ -1,12 +1,29 @@ -import { apiLogout } from '~constants/api' +import { API, GET_PATHS, apiLogout } from '~constants/api' import { api } from '~constants/mochi' -import { Button, Separator, Typography, useLoginWidget } from '@mochi-ui/core' +import { + ActionBar, + ActionBarActionGroup, + ActionBarBody, + ActionBarCancelButton, + ActionBarConfirmButton, + ActionBarContent, + ActionBarDescription, + ActionBarIcon, + Button, + Separator, + Typography, + toast, + useLoginWidget, +} from '@mochi-ui/core' import { useRouter } from 'next/router' -import React from 'react' +import React, { useCallback, useEffect } from 'react' import { FormProvider, useForm } from 'react-hook-form' import { ROUTES } from '~constants/routes' -import { platformGroupList, targetGroupList } from '~constants/settings' -import { GeneralFormValue } from './types' +import { useFetchGeneralSettings } from '~hooks/settings/useFetchGeneralSettings' +import { + ResponseGeneralSettingData, + ResponseUserGeneralSettingResponse, +} from '~types/mochi-schema' import { MoneySource } from './MoneySource' import { ReceiverPlatform } from './ReceiverPlatform' import { TokenPriority } from './TokenPriority' @@ -16,65 +33,93 @@ import { TransactionPrivacy } from './TransactionPrivacy' import { SocialAccountsPrivacy } from './SocialAccountsPrivacy' import { WalletsPrivacy } from './WalletsPrivacy' +const SETTINGS_GENERAL_FORM_ID = 'settings-general-form' + export const GeneralPage = () => { - const form = useForm({ + const form = useForm({ mode: 'all', - defaultValues: { - defaultMoneySource: 'mochi', - defaultReceiverPlatform: 'discord', - defaultTokenPriority: [{ id: '941f0fb1-00da-49dc-a538-5e81fc874cb4' }], - transactionPrivacy: { - general_target_group: targetGroupList[0].key, - general_platform_group: platformGroupList[0].key, - custom_settings: [], - }, - socialAccountsPrivacy: { - general_target_group: targetGroupList[0].key, - general_platform_group: platformGroupList[0].key, - custom_settings: [], - }, - walletsPrivacy: { - general_target_group: targetGroupList[0].key, - general_platform_group: platformGroupList[0].key, - custom_settings: [], - }, - }, }) - const { handleSubmit, control, watch } = form + const { + handleSubmit, + reset, + formState: { isSubmitting, isDirty }, + } = form - const { logout } = useLoginWidget() + const { profile, logout } = useLoginWidget() const { replace } = useRouter() + const { data: settings } = useFetchGeneralSettings(profile?.id) + + const resetData = useCallback( + (data?: ResponseGeneralSettingData) => { + if (!data) return + reset({ + ...data, + payment: { + ...data.payment, + prioritized_token: data.payment?.prioritized_token_ids?.map((id) => ({ + id, + })), + }, + }) + }, + [reset], + ) - const onSubmit = (data: any) => { - alert(JSON.stringify(data)) + const onUpdateSettings = (data: ResponseGeneralSettingData) => { + if (!profile?.id) return + const payload = { + payment: { + ...data.payment, + token_priorities: + data.payment?.prioritized_token?.map((each) => each.id) || [], + }, + privacy: { + ...data.privacy, + }, + } + return API.MOCHI.put(payload, GET_PATHS.UPDATE_GENERAL_SETTINGS(profile.id)) + .json((r: ResponseUserGeneralSettingResponse) => { + resetData(r.data) + }) + .catch((e) => { + const err = JSON.parse(e.message) + toast({ + description: err.msg, + scheme: 'danger', + }) + }) } - return ( - <> - -
-
- Payment setting + useEffect(() => { + resetData(settings) + }, [resetData, settings]) -
- - -
+ return ( + + +
+ Payment setting - - - +
+ + +
- + + + - Privacy + - - - -
-
+ Privacy + + + +
- +
+ + e.preventDefault()} + anchorClassName="left-0 right-0 -mb-8" + > + + + + Do you want to save these changes? + + + + reset()} + > + Reset + + + Save changes + + + + +
+ ) } diff --git a/apps/mochi-web/components/settings/general/MessageModal.tsx b/apps/mochi-web/components/settings/general/MessageModal.tsx index cf395c03f..ed9f80d17 100644 --- a/apps/mochi-web/components/settings/general/MessageModal.tsx +++ b/apps/mochi-web/components/settings/general/MessageModal.tsx @@ -22,7 +22,7 @@ import { Controller, useForm } from 'react-hook-form' import { zodResolver } from '@hookform/resolvers/zod' import { z } from 'zod' import { actionList } from '~constants/settings' -import { Message } from './types' +import { ModelDefaultMessageSetting } from '~types/mochi-schema' const schema = z.object({ action: z.string().min(1, 'This field is required'), @@ -30,8 +30,8 @@ const schema = z.object({ }) interface Props { - defaultValues?: Message - onConfirm: (data: Message) => void + defaultValues?: ModelDefaultMessageSetting + onConfirm: (data: ModelDefaultMessageSetting) => void trigger: React.ReactNode } @@ -41,12 +41,12 @@ export const MessageModal = ({ trigger, }: Props) => { const [open, setOpen] = useState(false) - const { control, handleSubmit, reset } = useForm({ + const { control, handleSubmit, reset } = useForm({ defaultValues, resolver: zodResolver(schema), }) - const onSubmit = (data: Message) => { + const onSubmit = (data: ModelDefaultMessageSetting) => { setOpen(false) onConfirm(data) } diff --git a/apps/mochi-web/components/settings/general/MoneySource.tsx b/apps/mochi-web/components/settings/general/MoneySource.tsx index 380e6c431..0b10eb2da 100644 --- a/apps/mochi-web/components/settings/general/MoneySource.tsx +++ b/apps/mochi-web/components/settings/general/MoneySource.tsx @@ -1,3 +1,4 @@ +import { truncate } from '@dwarvesf/react-utils' import { Button, FormControl, @@ -9,31 +10,54 @@ import { SelectSeparator, SelectTrigger, SelectValue, + useLoginWidget, } from '@mochi-ui/core' import { PlusLine } from '@mochi-ui/icons' -import { Control, Controller } from 'react-hook-form' -import { GeneralFormValue } from './types' +import { Controller, useFormContext } from 'react-hook-form' +import { ResponseGeneralSettingData } from '~types/mochi-schema' +import { getPlatform } from '~utils/platform' -interface Props { - control: Control -} +export const MoneySource = () => { + const { profile } = useLoginWidget() + const { control, setValue } = useFormContext() -export const MoneySource = ({ control }: Props) => { return ( ( Default money source - { + setValue( + 'payment.default_money_source', + { + platform_identifier: value, + platform: + profile?.associated_accounts?.find( + (each) => each.platform_identifier === value, + )?.platform || 'mochi', + }, + { shouldDirty: true }, + ) + }} + > - + Mochi wallet - EVM | 0xfsf...few - SOL | 0xfsf...few + {profile?.associated_accounts?.map((each) => ( + + {getPlatform(each.platform).name} |{' '} + {truncate(each.platform_identifier, 10, true)} + + ))} @@ -162,15 +89,26 @@ export const TokenPriority = ({ control, watch }: Props) => { value={tokenQuery} onChange={(e) => setTokenQuery(e.target.value)} /> - {filteredTokenList.map((token) => ( - append({ id: token.id })} - > - {token.symbol} - - ))} + ( + append({ ...item, chain: undefined })} + > + {item.symbol} + + )} + ListEmpty={ +
+ + + No tokens + +
+ } + />
diff --git a/apps/mochi-web/components/settings/general/TransactionLimit.tsx b/apps/mochi-web/components/settings/general/TransactionLimit.tsx index 978a9dae1..3d9b93e85 100644 --- a/apps/mochi-web/components/settings/general/TransactionLimit.tsx +++ b/apps/mochi-web/components/settings/general/TransactionLimit.tsx @@ -7,104 +7,26 @@ import { } from '@mochi-ui/core' import { EditLine, TrashBinLine } from '@mochi-ui/icons' import React from 'react' -import { - Control, - Controller, - UseFormWatch, - useFieldArray, -} from 'react-hook-form' +import { Controller, useFieldArray, useFormContext } from 'react-hook-form' import { actionList } from '~constants/settings' import { utils as mochiUtils } from '@consolelabs/mochi-ui' -import { GeneralFormValue } from './types' +import { ResponseGeneralSettingData } from '~types/mochi-schema' import { TransactionLimitModal } from './TransactionLimitModal' -const tokenList = [ - { - id: '941f0fb1-00da-49dc-a538-5e81fc874cb4', - name: 'Icy', - symbol: 'ICY', - decimal: 18, - chain_id: '137', - native: false, - address: '0x8D57d71B02d71e1e449a0E459DE40473Eb8f4a90', - icon: 'https://cdn.discordapp.com/emojis/1049620715374133288.webp?size=240&quality=lossless', - coin_gecko_id: 'icy', - price: 1.5, - chain: { - id: '7303f2f8-b6d9-454d-aa92-880569fa5295', - chain_id: '137', - name: 'Polygon Mainnet', - symbol: 'MATIC', - rpc: 'https://polygon.llamarpc.com', - explorer: 'https://polygonscan.com', - icon: 'https://cdn.discordapp.com/emojis/928216430535671818.png?size=240&quality=lossless', - type: 'evm', - }, - }, - { - id: 'd0bf0f44-e951-4f81-8643-3e6b9b0841d8', - name: 'Ethereum', - symbol: 'ETH', - decimal: 18, - chain_id: '42161', - native: true, - address: '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE', - icon: '', - coin_gecko_id: 'ethereum', - price: 2129.75, - chain: { - id: 'b1065911-fd00-4424-bc7c-03868e6d1ed1', - chain_id: '42161', - name: 'Arbitrum', - symbol: 'ARB', - rpc: 'https://arbitrum.llamarpc.com', - explorer: 'https://arbiscan.io', - icon: '', - type: 'evm', - }, - }, - { - id: '61388b7c-5505-4fdf-8084-077422369a93', - name: 'Fantom', - symbol: 'FTM', - decimal: 18, - chain_id: '250', - native: true, - address: '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE', - icon: '', - coin_gecko_id: 'fantom', - price: 0.380276, - chain: { - id: '7303f2f8-b6d9-454d-aa92-880569fa5296', - chain_id: '250', - name: 'Fantom Opera', - symbol: 'FTM', - rpc: 'https://rpc.ftm.tools', - explorer: 'https://ftmscan.com', - icon: 'https://cdn.discordapp.com/emojis/928216448902508564.png?size=240&quality=lossless', - type: 'evm', - }, - }, -] - -interface Props { - control: Control - watch: UseFormWatch -} - -export const TransactionLimit = ({ control, watch }: Props) => { +export const TransactionLimit = () => { + const { control, watch } = useFormContext() const { fields, append, remove, update } = useFieldArray({ control, - name: 'transactionLimit', + name: 'payment.tx_limit_settings', }) - const enableTransactionLimit = watch('enableTransactionLimit') + const enableTransactionLimit = watch('payment.tx_limit_enable') return (
Set the limit for transaction ( { {(enableTransactionLimit ? fields : []).map((each, index) => ( { - const minToken = tokenList.find( - (token) => token.id === field.value.minToken, - ) - const minAmount = mochiUtils.formatDigit({ - value: field.value.minAmount, - }) - const minUsd = - Number(field.value.minAmount) * (minToken?.price || 0) - const minUsdValue = mochiUtils.formatUsdDigit(minUsd) - const maxToken = tokenList.find( - (token) => token.id === field.value.maxToken, - ) - const maxAmount = mochiUtils.formatDigit({ - value: field.value.maxAmount, - }) - const maxUsd = - Number(field.value.maxAmount) * (maxToken?.price || 0) - const maxUsdValue = mochiUtils.formatUsdDigit(maxUsd) - return ( -
-
- - { - actionList.find( - (action) => action.key === field.value.action, - )?.label - } + render={({ field }) => ( +
+
+ + { + actionList.find( + (action) => action.key === field.value.action, + )?.label + } + +
+ + Minimum value: $ + {mochiUtils.formatDigit({ value: field.value.min || 0 })}, + + + Maximum value: $ + {mochiUtils.formatDigit({ value: field.value.max || 0 })} -
- - Minimum value: {minAmount} {minToken?.symbol} (≈{' '} - {minUsdValue}), - - - Maximum value: {maxAmount} {maxToken?.symbol} (≈{' '} - {maxUsdValue}) - -
- update(index, data)} - trigger={ - - - - } - /> - remove(index)} - > - -
- ) - }} + update(index, data)} + trigger={ + + + + } + /> + remove(index)} + > + + +
+ )} /> ))} {!!enableTransactionLimit && ( append(data)} trigger={ diff --git a/apps/mochi-web/components/settings/general/TransactionPrivacy.tsx b/apps/mochi-web/components/settings/general/TransactionPrivacy.tsx index 03037960b..b7787b151 100644 --- a/apps/mochi-web/components/settings/general/TransactionPrivacy.tsx +++ b/apps/mochi-web/components/settings/general/TransactionPrivacy.tsx @@ -1,19 +1,12 @@ import { FormLabel } from '@mochi-ui/core' import React from 'react' -import { Control, UseFormWatch } from 'react-hook-form' -import { GeneralFormValue } from './types' import { PrivacySetting } from './PrivacySetting' -interface Props { - control: Control - watch: UseFormWatch -} - -export const TransactionPrivacy = ({ control, watch }: Props) => { +export const TransactionPrivacy = () => { return (
Transaction Privacy - +
) } diff --git a/apps/mochi-web/components/settings/general/WalletsPrivacy.tsx b/apps/mochi-web/components/settings/general/WalletsPrivacy.tsx index 31e2f3023..c1b6100af 100644 --- a/apps/mochi-web/components/settings/general/WalletsPrivacy.tsx +++ b/apps/mochi-web/components/settings/general/WalletsPrivacy.tsx @@ -1,19 +1,12 @@ import { FormLabel } from '@mochi-ui/core' import React from 'react' -import { Control, UseFormWatch } from 'react-hook-form' -import { GeneralFormValue } from './types' import { PrivacySetting } from './PrivacySetting' -interface Props { - control: Control - watch: UseFormWatch -} - -export const WalletsPrivacy = ({ control, watch }: Props) => { +export const WalletsPrivacy = () => { return (
Wallets - +
) } diff --git a/apps/mochi-web/components/settings/general/types.ts b/apps/mochi-web/components/settings/general/types.ts deleted file mode 100644 index b8cc0364a..000000000 --- a/apps/mochi-web/components/settings/general/types.ts +++ /dev/null @@ -1,40 +0,0 @@ -interface TokenPriority { - id: string -} - -export interface Message { - action: string - message: string -} - -export interface TransactionLimit { - action: string - minToken: string - minAmount: string - maxToken: string - maxAmount: string -} - -export interface CustomSettings { - platform: string - target_group: string -} - -export interface CustomPrivacy { - custom_settings: CustomSettings[] - general_platform_group: string - general_target_group: string -} - -export interface GeneralFormValue { - defaultMoneySource: string - defaultReceiverPlatform: string - defaultTokenPriority: TokenPriority[] - enableDefaultMessage: boolean - defaultMessage: Message[] - enableTransactionLimit: boolean - transactionLimit: TransactionLimit[] - transactionPrivacy: CustomPrivacy - socialAccountsPrivacy: CustomPrivacy - walletsPrivacy: CustomPrivacy -} diff --git a/apps/mochi-web/components/settings/notification/NotificationPage.tsx b/apps/mochi-web/components/settings/notification/NotificationPage.tsx index ce2df5159..4ea0bb706 100644 --- a/apps/mochi-web/components/settings/notification/NotificationPage.tsx +++ b/apps/mochi-web/components/settings/notification/NotificationPage.tsx @@ -113,7 +113,7 @@ export function NotificationPage() { const body: RequestUpdateNotificationSettingPayloadRequest = { enable: enable ?? false, platforms, - flag: restData as Record, + flags: restData as Record, } try { const { data: newSettings }: ResponseUserNotificationSettingResponse = @@ -306,7 +306,7 @@ export function NotificationPage() { Platform Notification - + Select the platform you want to receive the notification
diff --git a/apps/mochi-web/constants/api.ts b/apps/mochi-web/constants/api.ts index 11f9c9d65..22c192647 100644 --- a/apps/mochi-web/constants/api.ts +++ b/apps/mochi-web/constants/api.ts @@ -80,4 +80,8 @@ export const GET_PATHS = { `/profile/${profileId}/pay-requests/${code}/enable`, DISABLE_PAYMENT_REQUEST: (profileId: string, code: string) => `/profile/${profileId}/pay-requests/${code}/disable`, + GET_GENERAL_SETTINGS: (profileId: string) => + `/profiles/${profileId}/settings/general`, + UPDATE_GENERAL_SETTINGS: (profileId: string) => + `/profiles/${profileId}/settings/general`, } diff --git a/apps/mochi-web/hooks/settings/useFetchGeneralSettings.ts b/apps/mochi-web/hooks/settings/useFetchGeneralSettings.ts new file mode 100644 index 000000000..bae7a68d5 --- /dev/null +++ b/apps/mochi-web/hooks/settings/useFetchGeneralSettings.ts @@ -0,0 +1,22 @@ +import useSWR from 'swr' +import { API, GET_PATHS } from '~constants/api' +import { ResponseUserGeneralSettingResponse } from '~types/mochi-schema' + +export const SWR_KEY_FETCH_GENERAL_SETTINGS = 'SWR_KEY_FETCH_GENERAL_SETTINGS' + +export const useFetchGeneralSettings = (profileId?: string) => { + const { data, ...rest } = useSWR( + profileId ? [SWR_KEY_FETCH_GENERAL_SETTINGS, profileId] : null, + async ([_, id]: [any, string]) => { + if (!id) return [] + return API.MOCHI.get(GET_PATHS.GET_GENERAL_SETTINGS(id)).json( + (r) => r ?? [], + ) + }, + ) + + return { + data: data?.data, + ...rest, + } +} diff --git a/apps/mochi-web/pages/tip-network.tsx b/apps/mochi-web/pages/tip-network.tsx index fdedc8169..609a637a6 100644 --- a/apps/mochi-web/pages/tip-network.tsx +++ b/apps/mochi-web/pages/tip-network.tsx @@ -6,7 +6,6 @@ import { HOME_URL } from '~envs' import { SEO } from '~app/layout/seo' import { API } from '~constants/api' import dynamic from 'next/dynamic' -import { TransactionGraphData, TransactionEdge } from '~types/mochi-schema' import { GraphData, NodeObject } from 'react-force-graph-2d' import { useMemo, useState } from 'react' import { ProfileBar } from '~components/TipNetwork/ProfileBar' @@ -18,6 +17,28 @@ import { TipNetworkData, } from '~types/tip-graph' import { useDisclosure } from '@dwarvesf/react-hooks' +import { Profile } from '@consolelabs/mochi-rest' + +export interface TransactionNode { + profile_id: string + total_volume: number + spend_volume: number + receive_volume: number + profile: Profile +} + +export interface TransactionEdge { + from_profile_id: string + to_profile_id: string + total_volume: number + spend: number + receive: number +} + +export interface TransactionGraphData { + nodes: TransactionNode[] + edges: TransactionEdge[] +} const TipNetworkGraph = dynamic( () => @@ -142,7 +163,7 @@ export default function TipNetwork({ data }: TipNetworkProps) { description="Tip network from server" url={`${HOME_URL}/tip-network/`} /> -
+
+ flags: Record platforms: string[] } @@ -2541,24 +2536,3 @@ export interface UuidNullUUID { /** Valid is true if UUID is not NULL */ valid?: boolean } - -export interface TransactionNode { - profile_id: string - total_volume: number - spend_volume: number - receive_volume: number - profile: Profile -} - -export interface TransactionEdge { - from_profile_id: string - to_profile_id: string - total_volume: number - spend: number - receive: number -} - -export interface TransactionGraphData { - nodes: TransactionNode[] - edges: TransactionEdge[] -} diff --git a/apps/mochi-web/utils/platform.ts b/apps/mochi-web/utils/platform.ts index 192aefdda..b980f7ba3 100644 --- a/apps/mochi-web/utils/platform.ts +++ b/apps/mochi-web/utils/platform.ts @@ -29,6 +29,12 @@ export const getPlatform = (key?: string) => { name: 'App', } } + case Platform.EvmChain: { + return { + icon: '', + name: 'EVM', + } + } default: return { icon: '',