diff --git a/public/locales/en/settings.json b/public/locales/en/settings.json index daca584c..cb2802ab 100644 --- a/public/locales/en/settings.json +++ b/public/locales/en/settings.json @@ -1,4 +1,5 @@ { + "applyInviteCode": "Apply Invite Code", "settings": "Settings", "seed-words": "Seed words", "send-logs": "Submit an Issue & Logs", @@ -9,6 +10,7 @@ "logs": "Logs", "open-logs-directory": "Open logs directory", "debug-info": "Debug", + "disconnect": "Disconnect from Airdrop", "hardware-status": "Hardware status", "update-versions": "Update versions", "refresh-versions": "Refresh versions", @@ -49,6 +51,7 @@ "reset-settings": "Reset Settings", "reset-permanently": "Are you sure you want to reset all settings permanently?", "reset-wallet": "Reset wallet", + "inviteCode": "Invite Code", "experimental-title": "Experimental Features", "experimental-warning": "⚠️ Warning: These features are under active development and could behave unpredictably. Please proceed carefully.", "connected-to-tari": "Connected to the Tari Network", diff --git a/src/containers/Airdrop/Settings/Logout.tsx b/src/containers/Airdrop/Settings/Logout.tsx index 06adf817..90ff5c45 100644 --- a/src/containers/Airdrop/Settings/Logout.tsx +++ b/src/containers/Airdrop/Settings/Logout.tsx @@ -1,18 +1,34 @@ import { Button } from '@app/components/elements/Button'; +import { Typography } from '@app/components/elements/Typography'; +import { + SettingsGroupContent, + SettingsGroupTitle, + SettingsGroupWrapper, +} from '@app/containers/Settings/components/SettingsGroup.styles'; import { useAirdropStore } from '@app/store/useAirdropStore'; import { useAppConfigStore } from '@app/store/useAppConfigStore'; +import { useTranslation } from 'react-i18next'; export default function AirdropLogout() { + const { t } = useTranslation(['settings'], { useSuspense: false }); + const airdropUIEnabled = useAppConfigStore((s) => s.airdrop_ui_enabled); const logout = useAirdropStore((state) => state.logout); const { userDetails } = useAirdropStore(); if (!airdropUIEnabled || !userDetails) return null; return ( -
- -
+ + + {t('connection')} + + +
+ +
+
+
); } diff --git a/src/containers/Settings/AirdropSettings.tsx b/src/containers/Settings/AirdropSettings.tsx new file mode 100644 index 00000000..0c5800df --- /dev/null +++ b/src/containers/Settings/AirdropSettings.tsx @@ -0,0 +1,9 @@ +import { useAirdropStore } from '@app/store/useAirdropStore'; +import AirdropLogout from '../Airdrop/Settings/Logout'; +import { ApplyInviteCode } from './sections/airdrop/ApplyInviteCode'; + +export const AirdropSettings = () => { + const { authUuid } = useAirdropStore(); + + return <>{authUuid ? : }; +}; diff --git a/src/containers/Settings/GeneralSettings.tsx b/src/containers/Settings/GeneralSettings.tsx index 7e502ceb..2c2505c4 100644 --- a/src/containers/Settings/GeneralSettings.tsx +++ b/src/containers/Settings/GeneralSettings.tsx @@ -2,7 +2,6 @@ import AirdropPermissionSettings from './sections/general/AirdropPermissionSetti import LogsSettings from './sections/general/LogsSettings.tsx'; import LanguageSettings from './sections/general/LanguageSettings.tsx'; import { ResetSettingsButton } from './sections/general/ResetSettingsButton.tsx'; -import AirdropLogout from '../Airdrop/Settings/Logout.tsx'; export const GeneralSettings = () => { return ( @@ -11,7 +10,6 @@ export const GeneralSettings = () => { - ); }; diff --git a/src/containers/Settings/SettingsModal.tsx b/src/containers/Settings/SettingsModal.tsx index 0e2c85c4..f1443f2a 100644 --- a/src/containers/Settings/SettingsModal.tsx +++ b/src/containers/Settings/SettingsModal.tsx @@ -17,11 +17,14 @@ import { WalletSettings } from './WalletSettings.tsx'; import { SettingsType } from './types.ts'; import { Container, ContentContainer, HeaderContainer, SectionWrapper, variants } from './SettingsModal.styles.ts'; +import { AirdropSettings } from './AirdropSettings.tsx'; +import { useAppConfigStore } from '@app/store/useAppConfigStore.ts'; export default function SettingsModal() { const { t } = useTranslation(['settings'], { useSuspense: false }); const isSettingsOpen = useAppStateStore((s) => s.isSettingsOpen); const setIsSettingsOpen = useAppStateStore((s) => s.setIsSettingsOpen); + const airdropUIEnabled = useAppConfigStore((s) => s.airdrop_ui_enabled); const [activeSection, setActiveSection] = useState('mining'); @@ -29,6 +32,7 @@ export default function SettingsModal() { const generalMarkup = activeSection === 'general' ? : null; const walletMarkup = activeSection === 'wallet' ? : null; const experimentalMarkup = activeSection === 'experimental' ? : null; + const airdropMarkup = airdropUIEnabled && activeSection === 'airdrop' ? : null; function onOpenChange() { if (isSettingsOpen) { @@ -56,6 +60,7 @@ export default function SettingsModal() { {generalMarkup} {walletMarkup} {experimentalMarkup} + {airdropMarkup} diff --git a/src/containers/Settings/sections/airdrop/ApplyInviteCode.tsx b/src/containers/Settings/sections/airdrop/ApplyInviteCode.tsx new file mode 100644 index 00000000..dfc9f0a2 --- /dev/null +++ b/src/containers/Settings/sections/airdrop/ApplyInviteCode.tsx @@ -0,0 +1,90 @@ +import { SettingsGroupContent, SettingsGroupTitle, SettingsGroupWrapper } from '../../components/SettingsGroup.styles'; +import { Typography } from '@app/components/elements/Typography'; +import { useTranslation } from 'react-i18next'; +import { useCallback, useEffect, useState } from 'react'; +import { Input } from '@app/components/elements/inputs/Input'; +import { Button } from '@app/components/elements/Button'; +import { v4 as uuidv4 } from 'uuid'; +import { useAirdropStore } from '@app/store/useAirdropStore'; +import { Stack } from '@app/components/elements/Stack'; +import { useAppConfigStore } from '@app/store/useAppConfigStore'; + +export const ApplyInviteCode = () => { + const { t } = useTranslation(['settings'], { useSuspense: false }); + const setAllowTelemetry = useAppConfigStore((s) => s.setAllowTelemetry); + + const [claimCode, setClaimCode] = useState(''); + + const { authUuid, setAuthUuid, setAirdropTokens, setUserPoints, backendInMemoryConfig } = useAirdropStore(); + + const handleAuth = useCallback( + (code?: string) => { + const token = uuidv4(); + if (backendInMemoryConfig?.airdropTwitterAuthUrl) { + setAuthUuid(token); + setAllowTelemetry(true); + + open( + `${backendInMemoryConfig?.airdropTwitterAuthUrl}?tauri=${token}${code ? `&universeReferral=${code}` : ''}` + ); + } + }, + [backendInMemoryConfig?.airdropTwitterAuthUrl, setAllowTelemetry, setAuthUuid] + ); + + useEffect(() => { + if (authUuid && backendInMemoryConfig?.airdropApiUrl) { + const interval = setInterval(() => { + if (authUuid) { + fetch(`${backendInMemoryConfig?.airdropApiUrl}/auth/twitter/get-token/${authUuid}`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + }) + .then((response) => response.json()) + .then((data) => { + if (!data.error) { + clearInterval(interval); + setAirdropTokens(data); + } + }); + } + }, 1000); + const timeout = setTimeout( + () => { + clearInterval(interval); + setAuthUuid(''); + }, + 1000 * 60 * 5 + ); + + return () => { + clearInterval(interval); + clearTimeout(timeout); + }; + } + }, [authUuid, backendInMemoryConfig?.airdropApiUrl, setAirdropTokens, setAuthUuid, setUserPoints]); + + return ( + + + {t('inviteCode')} + + + + setClaimCode(event.target.value)} + style={{ maxWidth: '20rem' }} + > + + + + + ); +}; diff --git a/src/containers/Settings/types.ts b/src/containers/Settings/types.ts index 5e3e9c4a..a51f1be6 100644 --- a/src/containers/Settings/types.ts +++ b/src/containers/Settings/types.ts @@ -1,3 +1,3 @@ -export const SETTINGS_TYPES = ['mining', 'general', 'wallet', 'experimental'] as const; +export const SETTINGS_TYPES = ['mining', 'general', 'wallet', 'experimental', 'airdrop'] as const; type SettingsTuple = typeof SETTINGS_TYPES; export type SettingsType = SettingsTuple[number];