added useAuth as export

This commit is contained in:
PhilReact 2025-06-27 17:27:54 +03:00
parent 8da43d1fa8
commit 78af0d1750
8 changed files with 335 additions and 172 deletions

View File

@ -5,7 +5,7 @@ import React, {
useEffect,
useMemo,
} from "react";
import { useAuth, UseAuthProps } from "../hooks/useAuth";
import { useAuth, UseAuthProps } from "../hooks/useInitializeAuth";
import { useResources } from "../hooks/useResources";
import { useAppInfo } from "../hooks/useAppInfo";
import { useIdentifiers } from "../hooks/useIdentifiers";
@ -15,10 +15,8 @@ import { IndexManager } from "../components/IndexManager/IndexManager";
import { useIndexes } from "../hooks/useIndexes";
import { useProgressStore } from "../state/video";
import { GlobalPipPlayer } from "../hooks/useGlobalPipPlayer";
import { Location, NavigateFunction } from "react-router-dom";
import { MultiPublishDialog } from "../components/MultiPublish/MultiPublishDialog";
import { useMultiplePublishStore } from "../state/multiplePublish";
import { useGlobalPlayerStore } from "../state/pip";
// ✅ Define Global Context Type
interface GlobalContextType {
@ -28,7 +26,7 @@ interface GlobalContextType {
identifierOperations: ReturnType<typeof useIdentifiers>;
persistentOperations: ReturnType<typeof usePersistentStore>;
indexOperations: ReturnType<typeof useIndexes>;
enableGlobalVideoFeature: boolean
enableGlobalVideoFeature: boolean;
}
// ✅ Define Config Type for Hook Options
@ -39,7 +37,7 @@ interface GlobalProviderProps {
auth?: UseAuthProps;
appName: string;
publicSalt: string;
enableGlobalVideoFeature?: boolean
enableGlobalVideoFeature?: boolean;
};
toastStyle?: CSSProperties;
@ -48,9 +46,6 @@ interface GlobalProviderProps {
// ✅ Create Context with Proper Type
export const GlobalContext = createContext<GlobalContextType | null>(null);
// 🔹 Global Provider (Handles Multiple Hooks)
export const GlobalProvider = ({
children,
@ -59,7 +54,7 @@ export const GlobalProvider = ({
}: GlobalProviderProps) => {
// ✅ Call hooks and pass in options dynamically
const auth = useAuth(config?.auth || {});
const isPublishing = useMultiplePublishStore((s)=> s.isPublishing);
const isPublishing = useMultiplePublishStore((s) => s.isPublishing);
const appInfo = useAppInfo(config.appName, config?.publicSalt);
const lists = useResources();
const identifierOperations = useIdentifiers(
@ -80,9 +75,16 @@ export const GlobalProvider = ({
identifierOperations,
persistentOperations,
indexOperations,
enableGlobalVideoFeature: config?.enableGlobalVideoFeature || false
enableGlobalVideoFeature: config?.enableGlobalVideoFeature || false,
}),
[auth, lists, appInfo, identifierOperations, persistentOperations, config?.enableGlobalVideoFeature]
[
auth,
lists,
appInfo,
identifierOperations,
persistentOperations,
config?.enableGlobalVideoFeature,
]
);
const { clearOldProgress } = useProgressStore();
@ -91,18 +93,10 @@ export const GlobalProvider = ({
}, []);
return (
<GlobalContext.Provider value={contextValue}>
{config?.enableGlobalVideoFeature && (
<GlobalPipPlayer />
)}
{isPublishing && (
<MultiPublishDialog />
)}
{config?.enableGlobalVideoFeature && <GlobalPipPlayer />}
{isPublishing && <MultiPublishDialog />}
<Toaster
position="top-center"
toastOptions={{
@ -115,7 +109,6 @@ export const GlobalProvider = ({
{children}
</GlobalContext.Provider>
);
};

View File

@ -1,157 +1,83 @@
import React, { useCallback, useEffect, useMemo, useRef } from "react";
import { useCallback, useMemo } from "react";
import { useAuthStore } from "../state/auth";
import { userAccountInfo } from "./useInitializeAuth";
// ✅ Define Types
/**
* Configuration for balance retrieval behavior.
*/
export type BalanceSetting =
| {
/** If `true`, the balance will be fetched only once when the app loads. */
onlyOnMount: true;
/** `interval` cannot be set when `onlyOnMount` is `true`. */
interval?: never;
}
| {
/** If `false` or omitted, balance will be updated periodically. */
onlyOnMount?: false;
/** The time interval (in milliseconds) for balance updates. */
interval?: number;
};
interface userAccountInfo {
address: string;
publicKey: string
}
export interface UseAuthProps {
balanceSetting?: BalanceSetting;
/** User will be prompted for authentication on start-up */
authenticateOnMount?: boolean;
userAccountInfo?: userAccountInfo | null
}
export const useAuth = ({ balanceSetting, authenticateOnMount = true, userAccountInfo = null }: UseAuthProps) => {
export const useAuth = () => {
const address = useAuthStore((s) => s.address);
const publicKey = useAuthStore((s) => s.publicKey);
const name = useAuthStore((s) => s.name);
const avatarUrl = useAuthStore((s) => s.avatarUrl);
const balance = useAuthStore((s) => s.balance);
const publicKey = useAuthStore((s) => s.publicKey);
const name = useAuthStore((s) => s.name);
const avatarUrl = useAuthStore((s) => s.avatarUrl);
const isLoadingUser = useAuthStore((s) => s.isLoadingUser);
const isLoadingInitialBalance = useAuthStore((s) => s.isLoadingInitialBalance);
const errorLoadingUser = useAuthStore((s) => s.errorLoadingUser);
const isLoadingUser = useAuthStore((s) => s.isLoadingUser);
const errorLoadingUser = useAuthStore((s) => s.errorLoadingUser);
const setErrorLoadingUser = useAuthStore((s) => s.setErrorLoadingUser);
const setIsLoadingUser = useAuthStore((s) => s.setIsLoadingUser);
const setUser = useAuthStore((s) => s.setUser);
const setName = useAuthStore((s) => s.setName);
const authenticateUser = useCallback(
async (userAccountInfo?: userAccountInfo) => {
try {
setErrorLoadingUser(null);
setIsLoadingUser(true);
const setErrorLoadingUser = useAuthStore((s) => s.setErrorLoadingUser);
const setIsLoadingUser = useAuthStore((s) => s.setIsLoadingUser);
const setUser = useAuthStore((s) => s.setUser);
const setBalance = useAuthStore((s) => s.setBalance);
const account =
userAccountInfo ||
(await qortalRequest({
action: "GET_USER_ACCOUNT",
}));
const balanceSetIntervalRef = useRef<null | ReturnType<typeof setInterval>>(null);
const authenticateUser = useCallback(async (userAccountInfo?: userAccountInfo) => {
try {
setErrorLoadingUser(null);
setIsLoadingUser(true);
const account = userAccountInfo || await qortalRequest({
action: "GET_USER_ACCOUNT",
});
if (account?.address) {
const nameData = await qortalRequest({
action: "GET_PRIMARY_NAME",
address: account.address,
});
setUser({ ...account, name: nameData || "" });
if (account?.address) {
const nameData = await qortalRequest({
action: "GET_PRIMARY_NAME",
address: account.address,
});
setUser({ ...account, name: nameData || "" });
}
} catch (error) {
setErrorLoadingUser(
error instanceof Error ? error.message : "Unable to authenticate"
);
} finally {
setIsLoadingUser(false);
}
} catch (error) {
setErrorLoadingUser(
error instanceof Error ? error.message : "Unable to authenticate"
);
} finally {
setIsLoadingUser(false);
}
}, [setErrorLoadingUser, setIsLoadingUser, setUser]);
},
[setErrorLoadingUser, setIsLoadingUser, setUser]
);
const getBalance = useCallback(async (address: string): Promise<number> => {
try {
const response = await qortalRequest({
action: "GET_BALANCE",
address,
});
const userBalance = Number(response) || 0
setBalance(userBalance);
return userBalance
} catch (error) {
setBalance(0);
return 0
}
}, [setBalance]);
const switchName = useCallback(
async (name: string) => {
if (!name) throw new Error("No name provided");
const response = await fetch(`/names/${name}`);
if (!response?.ok) throw new Error("Error fetching name details");
const nameInfo = await response.json();
const currentAddress = useAuthStore.getState().address;
const balanceSetInterval = useCallback((address: string, interval: number) => {
try {
if (balanceSetIntervalRef.current) {
clearInterval(balanceSetIntervalRef.current);
}
let isCalling = false;
balanceSetIntervalRef.current = setInterval(async () => {
if (isCalling) return;
isCalling = true;
await getBalance(address);
isCalling = false;
}, interval);
} catch (error) {
console.error(error);
}
}, [getBalance]);
useEffect(() => {
if (authenticateOnMount) {
authenticateUser();
}
if(userAccountInfo?.address && userAccountInfo?.publicKey){
authenticateUser(userAccountInfo);
}
}, [authenticateOnMount, authenticateUser, userAccountInfo?.address, userAccountInfo?.publicKey]);
useEffect(() => {
if (address && (balanceSetting?.onlyOnMount || (balanceSetting?.interval && !isNaN(balanceSetting?.interval)))) {
getBalance(address);
}
if (address && balanceSetting?.interval !== undefined && !isNaN(balanceSetting.interval)) {
balanceSetInterval(address, balanceSetting.interval);
}
}, [balanceSetting?.onlyOnMount, balanceSetting?.interval, address, getBalance, balanceSetInterval]);
const manualGetBalance = useCallback(async () : Promise<number | Error> => {
if(!address) throw new Error('Not authenticated')
const res = await getBalance(address)
return res
}, [address])
return useMemo(() => ({
address,
publicKey,
name,
avatarUrl,
balance,
isLoadingUser,
isLoadingInitialBalance,
errorMessageLoadingUser: errorLoadingUser,
authenticateUser,
getBalance: manualGetBalance,
}), [
address,
publicKey,
name,
avatarUrl,
balance,
isLoadingUser,
isLoadingInitialBalance,
errorLoadingUser,
authenticateUser,
manualGetBalance,
]);
if (nameInfo?.owner !== currentAddress)
throw new Error(`This account does not own the name ${name}`);
setName(name);
},
[setName]
);
return useMemo(
() => ({
address,
publicKey,
name,
avatarUrl,
isLoadingUser,
errorMessageLoadingUser: errorLoadingUser,
authenticateUser,
switchName,
}),
[
address,
publicKey,
name,
avatarUrl,
isLoadingUser,
errorLoadingUser,
authenticateUser,
switchName,
]
);
};

48
src/hooks/useBalance.tsx Normal file
View File

@ -0,0 +1,48 @@
import { useCallback, useMemo } from "react";
import { useAuthStore } from "../state/auth";
export const useQortBalance = () => {
const address = useAuthStore((s) => s.address);
const setBalance = useAuthStore((s) => s.setBalance);
const isLoadingInitialBalance = useAuthStore(
(s) => s.isLoadingInitialBalance
);
const setIsLoadingBalance = useAuthStore((s) => s.setIsLoadingBalance);
const qortBalance = useAuthStore((s) => s.balance);
const getBalance = useCallback(
async (address: string): Promise<number> => {
try {
setIsLoadingBalance(true);
const response = await qortalRequest({
action: "GET_BALANCE",
address,
});
const userBalance = Number(response) || 0;
setBalance(userBalance);
return userBalance;
} catch (error) {
setBalance(0);
return 0;
} finally {
setIsLoadingBalance(false);
}
},
[setBalance]
);
const manualGetBalance = useCallback(async (): Promise<number | Error> => {
if (!address) throw new Error("Not authenticated");
const res = await getBalance(address);
return res;
}, [address]);
return useMemo(
() => ({
value: qortBalance,
getBalance: manualGetBalance,
isLoading: isLoadingInitialBalance,
}),
[qortBalance, manualGetBalance, isLoadingInitialBalance]
);
};

View File

@ -0,0 +1,185 @@
import React, { useCallback, useEffect, useMemo, useRef } from "react";
import { useAuthStore } from "../state/auth";
// ✅ Define Types
/**
* Configuration for balance retrieval behavior.
*/
export type BalanceSetting =
| {
/** If `true`, the balance will be fetched only once when the app loads. */
onlyOnMount: true;
/** `interval` cannot be set when `onlyOnMount` is `true`. */
interval?: never;
}
| {
/** If `false` or omitted, balance will be updated periodically. */
onlyOnMount?: false;
/** The time interval (in milliseconds) for balance updates. */
interval?: number;
};
export interface userAccountInfo {
address: string;
publicKey: string;
}
export interface UseAuthProps {
balanceSetting?: BalanceSetting;
/** User will be prompted for authentication on start-up */
authenticateOnMount?: boolean;
userAccountInfo?: userAccountInfo | null;
}
export const useAuth = ({
balanceSetting,
authenticateOnMount = true,
userAccountInfo = null,
}: UseAuthProps) => {
const address = useAuthStore((s) => s.address);
const publicKey = useAuthStore((s) => s.publicKey);
const name = useAuthStore((s) => s.name);
const avatarUrl = useAuthStore((s) => s.avatarUrl);
const isLoadingUser = useAuthStore((s) => s.isLoadingUser);
const errorLoadingUser = useAuthStore((s) => s.errorLoadingUser);
const setIsLoadingBalance = useAuthStore((s) => s.setIsLoadingBalance);
const setErrorLoadingUser = useAuthStore((s) => s.setErrorLoadingUser);
const setIsLoadingUser = useAuthStore((s) => s.setIsLoadingUser);
const setUser = useAuthStore((s) => s.setUser);
const setBalance = useAuthStore((s) => s.setBalance);
const balanceSetIntervalRef = useRef<null | ReturnType<typeof setInterval>>(
null
);
const authenticateUser = useCallback(
async (userAccountInfo?: userAccountInfo) => {
try {
setErrorLoadingUser(null);
setIsLoadingUser(true);
const account =
userAccountInfo ||
(await qortalRequest({
action: "GET_USER_ACCOUNT",
}));
if (account?.address) {
const nameData = await qortalRequest({
action: "GET_PRIMARY_NAME",
address: account.address,
});
setUser({ ...account, name: nameData || "" });
}
} catch (error) {
setErrorLoadingUser(
error instanceof Error ? error.message : "Unable to authenticate"
);
} finally {
setIsLoadingUser(false);
}
},
[setErrorLoadingUser, setIsLoadingUser, setUser]
);
const getBalance = useCallback(
async (address: string): Promise<number> => {
try {
setIsLoadingBalance(true);
const response = await qortalRequest({
action: "GET_BALANCE",
address,
});
const userBalance = Number(response) || 0;
setBalance(userBalance);
return userBalance;
} catch (error) {
setBalance(0);
return 0;
} finally {
setIsLoadingBalance(false);
}
},
[setBalance]
);
const balanceSetInterval = useCallback(
(address: string, interval: number) => {
try {
if (balanceSetIntervalRef.current) {
clearInterval(balanceSetIntervalRef.current);
}
let isCalling = false;
balanceSetIntervalRef.current = setInterval(async () => {
if (isCalling) return;
isCalling = true;
await getBalance(address);
isCalling = false;
}, interval);
} catch (error) {
console.error(error);
}
},
[getBalance]
);
useEffect(() => {
if (authenticateOnMount) {
authenticateUser();
}
if (userAccountInfo?.address && userAccountInfo?.publicKey) {
authenticateUser(userAccountInfo);
}
}, [
authenticateOnMount,
authenticateUser,
userAccountInfo?.address,
userAccountInfo?.publicKey,
]);
useEffect(() => {
if (
address &&
(balanceSetting?.onlyOnMount ||
(balanceSetting?.interval && !isNaN(balanceSetting?.interval)))
) {
getBalance(address);
}
if (
address &&
balanceSetting?.interval !== undefined &&
!isNaN(balanceSetting.interval)
) {
balanceSetInterval(address, balanceSetting.interval);
}
}, [
balanceSetting?.onlyOnMount,
balanceSetting?.interval,
address,
getBalance,
balanceSetInterval,
]);
return useMemo(
() => ({
address,
publicKey,
name,
avatarUrl,
isLoadingUser,
errorMessageLoadingUser: errorLoadingUser,
authenticateUser,
}),
[
address,
publicKey,
name,
avatarUrl,
isLoadingUser,
errorLoadingUser,
authenticateUser,
]
);
};

View File

@ -7,7 +7,6 @@ import { ReturnType } from "../components/ResourceList/ResourceListDisplay";
import { useCacheStore } from "../state/cache";
import { useMultiplePublishStore, usePublishStatusStore } from "../state/multiplePublish";
import { ResourceToPublish } from "../types/qortalRequests/types";
import { MultiplePublishError } from "../components/MultiPublish/MultiPublishDialog";
interface StoredPublish {
qortalMetadata: QortalMetadata;

View File

@ -9,6 +9,8 @@ export { useAudioPlayerHotkeys } from './components/AudioPlayer/useAudioPlayerHo
export { VideoPlayerParent as VideoPlayer } from './components/VideoPlayer/VideoPlayerParent';
export { useListReturn } from './hooks/useListData';
export { useAllResourceStatus } from './hooks/useAllResourceStatus';
export { useQortBalance } from './hooks/useBalance';
export { useAuth } from './hooks/useAuth';
import './index.css'
export { executeEvent, subscribeToEvent, unsubscribeFromEvent } from './utils/events';
export { formatBytes, formatDuration } from './utils/numbers';

View File

@ -23,6 +23,7 @@ interface AuthState {
setIsLoadingUser: (loading: boolean) => void;
setIsLoadingBalance: (loading: boolean) => void;
setErrorLoadingUser: (error: string | null) => void;
setName: (name: string | null) => void;
}
// ✅ Typed Zustand Store
@ -43,4 +44,11 @@ export const useAuthStore = create<AuthState>((set) => ({
setIsLoadingUser: (loading) => set({ isLoadingUser: loading }),
setIsLoadingBalance: (loading) => set({ isLoadingInitialBalance: loading }),
setErrorLoadingUser: (error) => set({ errorLoadingUser: error }),
setName: (name) =>
set({
name,
avatarUrl: !name
? null
: `/arbitrary/THUMBNAIL/${encodeURIComponent(name)}/qortal_avatar?async=true`,
}),
}));

View File

@ -1,4 +1,6 @@
export const createAvatarLink = (qortalName: string)=> {
export const createAvatarLink = (qortalName: string): string => {
if (!qortalName?.trim()) return '';
return `/arbitrary/THUMBNAIL/${encodeURIComponent(qortalName)}/qortal_avatar?async=true`
}