Skip to content

Commit

Permalink
fix(airdrop: order async flow
Browse files Browse the repository at this point in the history
  • Loading branch information
mmrrnn committed Nov 18, 2024
1 parent 9ebb238 commit 91f5778
Show file tree
Hide file tree
Showing 5 changed files with 91 additions and 69 deletions.
60 changes: 31 additions & 29 deletions src/hooks/airdrop/stateHelpers/useAirdropTokensRefresh.ts
Original file line number Diff line number Diff line change
@@ -1,39 +1,50 @@
import { useAirdropStore } from '@app/store/useAirdropStore';
import { AirdropTokens, useAirdropStore } from '@app/store/useAirdropStore';
import { useCallback, useEffect } from 'react';
import { invoke } from '@tauri-apps/api/tauri';
import * as Sentry from '@sentry/react';

export async function fetchAirdropTokens(airdropApiUrl: string, airdropTokens: AirdropTokens) {
const response = await fetch(`${airdropApiUrl}/auth/local/refresh`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
refreshToken: airdropTokens.refreshToken,
}),
});
if (!response.ok) {
throw new Error('Failed to refresh token');
}

const data: AirdropTokens = await response.json();
return data;
}

export function useHandleAirdropTokensRefresh() {
const { airdropTokens, setAirdropTokens } = useAirdropStore();
const syncedAidropWithBackend = useAirdropStore((s) => s.syncedWithBackend);

return useCallback(
(airdropApiUrl: string) => {
async (airdropApiUrl: string) => {
let fetchedAirdropTokens: AirdropTokens | undefined;
// 5 hours from now
const expirationLimit = new Date(new Date().getTime() + 1000 * 60 * 60 * 5);
const tokenExpirationTime = airdropTokens?.expiresAt && new Date(airdropTokens?.expiresAt * 1000);

const tokenHasExpired = tokenExpirationTime && tokenExpirationTime < expirationLimit;
if (airdropTokens && tokenHasExpired) {
fetch(`${airdropApiUrl}/auth/local/refresh`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
refreshToken: airdropTokens.refreshToken,
}),
})
.then((response) => response.json())
.then((data) => {
setAirdropTokens(data);
});
if (airdropTokens && (!syncedAidropWithBackend || tokenHasExpired)) {
try {
fetchedAirdropTokens = await fetchAirdropTokens(airdropApiUrl, airdropTokens);
} catch (error) {
console.error('Error refreshing airdrop tokens:', error);
}
}
await setAirdropTokens(fetchedAirdropTokens);
},
[airdropTokens, setAirdropTokens]
[airdropTokens, setAirdropTokens, syncedAidropWithBackend]
);
}
export function useAirdropTokensRefresh() {
const { airdropTokens, backendInMemoryConfig } = useAirdropStore();
const { backendInMemoryConfig } = useAirdropStore();

// Handle refreshing the access token
const handleRefresh = useHandleAirdropTokensRefresh();
Expand All @@ -43,13 +54,4 @@ export function useAirdropTokensRefresh() {
const interval = setInterval(() => handleRefresh(backendInMemoryConfig?.airdropApiUrl), 1000 * 60 * 60);
return () => clearInterval(interval);
}, [handleRefresh, backendInMemoryConfig?.airdropApiUrl]);

// Handle setting the access token
useEffect(() => {
if (!airdropTokens) return;
invoke('set_airdrop_access_token', { token: airdropTokens?.token }).catch((error) => {
Sentry.captureException(error);
console.error('Error getting airdrop tokens', error);
});
}, [airdropTokens]);
}
19 changes: 0 additions & 19 deletions src/hooks/airdrop/stateHelpers/useGetRustInMemoryConfig.ts

This file was deleted.

2 changes: 0 additions & 2 deletions src/hooks/airdrop/useAirdropSyncState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,8 @@ import { useAirdropTokensRefresh } from './stateHelpers/useAirdropTokensRefresh'
import { useAirdropUserPointsListener } from './stateHelpers/useAirdropUserPointsListener';
import { useGetAirdropUserDetails } from './stateHelpers/useGetAirdropUserDetails';
import { useGetReferralQuestPoints } from './stateHelpers/useGetReferralQuestPoints';
import { useGetRustInMemoryConfig } from './stateHelpers/useGetRustInMemoryConfig';

export const useAirdropSyncState = () => {
useGetRustInMemoryConfig();
useAirdropTokensRefresh();
useGetAirdropUserDetails();
useAirdropUserPointsListener();
Expand Down
30 changes: 22 additions & 8 deletions src/hooks/useSetUp.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as Sentry from '@sentry/react';
import { useCallback, useEffect, useRef, useState } from 'react';
import { useCallback, useEffect, useRef } from 'react';
import { listen } from '@tauri-apps/api/event';
import { invoke } from '@tauri-apps/api/tauri';

Expand All @@ -20,15 +20,21 @@ export function useSetUp() {
const setSettingUpFinished = useAppStateStore((s) => s.setSettingUpFinished);
const setSeenPermissions = useAirdropStore((s) => s.setSeenPermissions);
const fetchApplicationsVersionsWithRetry = useAppStateStore((s) => s.fetchApplicationsVersionsWithRetry);
const syncedAidropWithBackend = useAirdropStore((s) => s.syncedWithBackend);

const backendInMemoryConfig = useAirdropStore((s) => s.backendInMemoryConfig);
const fetchBackendInMemoryConfig = useAirdropStore((s) => s.fetchBackendInMemoryConfig);

useEffect(() => {
if (backendInMemoryConfig?.airdropApiUrl) {
handleRefreshAirdropTokens(backendInMemoryConfig.airdropApiUrl);
}
const refreshTokens = async () => {
const backendInMemoryConfig = await fetchBackendInMemoryConfig();
if (backendInMemoryConfig?.airdropApiUrl) {
await handleRefreshAirdropTokens(backendInMemoryConfig.airdropApiUrl);
}
};
refreshTokens();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [backendInMemoryConfig?.airdropApiUrl]);
}, []);

const clearStorage = useCallback(() => {
// clear all storage except airdrop data
const airdropStorage = localStorage.getItem('airdrop-store');
Expand Down Expand Up @@ -58,7 +64,7 @@ export function useSetUp() {
break;
}
});
if (isAfterAutoUpdate && !isInitializingRef.current) {
if (isAfterAutoUpdate && syncedAidropWithBackend && !isInitializingRef.current) {
isInitializingRef.current = true;
clearStorage();
invoke('setup_application').catch((e) => {
Expand All @@ -69,5 +75,13 @@ export function useSetUp() {
return () => {
unlistenPromise.then((unlisten) => unlisten());
};
}, [clearStorage, handlePostSetup, isAfterAutoUpdate, setCriticalError, setSeenPermissions, setSetupDetails]);
}, [
syncedAidropWithBackend,
clearStorage,
handlePostSetup,
isAfterAutoUpdate,
setCriticalError,
setSeenPermissions,
setSetupDetails,
]);
}
49 changes: 38 additions & 11 deletions src/store/useAirdropStore.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { createWithEqualityFn as create } from 'zustand/traditional';
import { persist } from 'zustand/middleware';
import { invoke } from '@tauri-apps/api/tauri';
import * as Sentry from '@sentry/react';

export const GIFT_GEMS = 5000;
export const REFERRAL_GEMS = 5000;
Expand Down Expand Up @@ -96,7 +98,7 @@ export interface UserDetails {
user: User;
}

interface AirdropTokens {
export interface AirdropTokens {
token: string;
refreshToken: string;
expiresAt?: number;
Expand Down Expand Up @@ -124,6 +126,7 @@ interface MiningPoint {

interface AirdropState {
authUuid: string;
syncedWithBackend: boolean;
airdropTokens?: AirdropTokens;
userDetails?: UserDetails;
userPoints?: UserPoints;
Expand All @@ -140,10 +143,10 @@ interface AirdropStore extends AirdropState {
setReferralQuestPoints: (referralQuestPoints: ReferralQuestPoints) => void;
setMiningRewardPoints: (miningRewardPoints?: MiningPoint) => void;
setAuthUuid: (authUuid: string) => void;
setAirdropTokens: (airdropToken: AirdropTokens) => void;
setAirdropTokens: (airdropToken?: AirdropTokens) => Promise<void>;
setUserDetails: (userDetails?: UserDetails) => void;
setUserPoints: (userPoints: UserPoints) => void;
setBackendInMemoryConfig: (config?: BackendInMemoryConfig) => void;
fetchBackendInMemoryConfig: (config?: BackendInMemoryConfig) => Promise<BackendInMemoryConfig | undefined>;
setReferralCount: (referralCount: ReferralCount) => void;
setFlareAnimationType: (flareAnimationType?: AnimationType) => void;
setBonusTiers: (bonusTiers: BonusTier[]) => void;
Expand All @@ -155,6 +158,7 @@ interface AirdropStore extends AirdropState {
const initialState: AirdropState = {
authUuid: '',
seenPermissions: false,
syncedWithBackend: false,
};

const clearState: Partial<AirdropState> = {
Expand All @@ -174,13 +178,26 @@ export const useAirdropStore = create<AirdropStore>()(
setBonusTiers: (bonusTiers) => set({ bonusTiers }),
setUserDetails: (userDetails) => set({ userDetails }),
setAuthUuid: (authUuid) => set({ authUuid }),
setAirdropTokens: (airdropTokens) =>
set({
airdropTokens: {
...airdropTokens,
expiresAt: parseJwt(airdropTokens.token).exp,
},
}),
setAirdropTokens: async (airdropTokens) => {
if (airdropTokens) {
try {
await invoke('set_airdrop_access_token', { token: airdropTokens.token });
} catch (error) {
Sentry.captureException(error);
console.error('Error getting airdrop tokens', error);
}
set({
syncedWithBackend: true,
airdropTokens: {
...airdropTokens,
expiresAt: parseJwt(airdropTokens.token).exp,
},
});
} else {
// User not connected
set({ syncedWithBackend: true });
}
},
setReferralCount: (referralCount) => set({ referralCount }),
setUserPoints: (userPoints) => set({ userPoints }),
setUserGems: (userGems: number) =>
Expand All @@ -194,7 +211,17 @@ export const useAirdropStore = create<AirdropStore>()(
userPoints: userPointsFormatted,
};
}),
setBackendInMemoryConfig: (backendInMemoryConfig) => set({ backendInMemoryConfig }),
fetchBackendInMemoryConfig: async () => {
let backendInMemoryConfig: BackendInMemoryConfig | undefined = undefined;
try {
backendInMemoryConfig = await invoke('get_app_in_memory_config', {});
set({ backendInMemoryConfig });
} catch (e) {
Sentry.captureException(e);
console.error('get_app_in_memory_config error:', e);
}
return backendInMemoryConfig;
},
setMiningRewardPoints: (miningRewardPoints) => set({ miningRewardPoints, flareAnimationType: 'BonusGems' }),
setSeenPermissions: (seenPermissions) => set({ seenPermissions }),
logout: () => set(clearState),
Expand Down

0 comments on commit 91f5778

Please sign in to comment.