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];