Files
Postiz-android/artifacts/postiz-mobile/context/PostizContext.tsx
T
billisdead c89f61a77f fix: store axios instance correctly in useState
An axios instance (returned by axios.create()) is itself a callable
function. React's useState setter treats any function argument as an
updater callback, calling it with the previous state instead of storing
it as the new value. This caused setClient(createClient(...)) to invoke
the axios instance with null, store the resulting Promise as client,
and produce "client.get is not a function (it is undefined)" at runtime.

Fix: wrap in an arrow function so React uses the instance as the return
value of the updater rather than as the updater itself.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-16 12:31:27 +02:00

145 lines
3.6 KiB
TypeScript

import axios, { AxiosInstance } from "axios";
import * as SecureStore from "expo-secure-store";
import React, {
createContext,
useCallback,
useContext,
useEffect,
useState,
} from "react";
const API_KEY_STORAGE = "postiz_api_key";
const BASE_URL_STORAGE = "postiz_base_url";
const DEFAULT_BASE_URL = "https://postiz.gyozamancave.fr/api/public/v1";
export interface PostizIntegration {
id: string;
name: string;
type: string;
picture?: string;
identifier?: string;
internalType?: string;
}
export interface PostizMediaItem {
id: string;
path: string;
}
export interface PostizPost {
id: string;
content: string;
state: "QUEUE" | "PUBLISHED" | "ERROR" | "DRAFT";
publishDate: string;
integration?: PostizIntegration;
integrations?: PostizIntegration[];
image?: PostizMediaItem[];
group?: string;
}
export interface PostizUploadResult {
id: string;
path: string;
}
interface PostizContextValue {
apiKey: string;
baseUrl: string;
isConfigured: boolean;
isLoading: boolean;
client: AxiosInstance | null;
saveSettings: (apiKey: string, baseUrl: string) => Promise<void>;
clearSettings: () => Promise<void>;
}
const PostizContext = createContext<PostizContextValue>({
apiKey: "",
baseUrl: DEFAULT_BASE_URL,
isConfigured: false,
isLoading: true,
client: null,
saveSettings: async () => {},
clearSettings: async () => {},
});
function createClient(apiKey: string, baseUrl: string): AxiosInstance {
const normalizedUrl = baseUrl.endsWith("/") ? baseUrl : baseUrl + "/";
const instance = axios.create({
baseURL: normalizedUrl,
headers: {
Authorization: `Bearer ${apiKey}`,
"Content-Type": "application/json",
},
timeout: 15000,
});
instance.interceptors.request.use((config) => {
console.log(">>> REQUEST:", config.method?.toUpperCase(), (config.baseURL || "") + (config.url || ""));
return config;
});
return instance;
}
export function PostizProvider({ children }: { children: React.ReactNode }) {
const [apiKey, setApiKey] = useState("");
const [baseUrl, setBaseUrl] = useState(DEFAULT_BASE_URL);
const [isLoading, setIsLoading] = useState(true);
const [client, setClient] = useState<AxiosInstance | null>(null);
useEffect(() => {
(async () => {
try {
const storedKey = await SecureStore.getItemAsync(API_KEY_STORAGE);
const storedUrl = await SecureStore.getItemAsync(BASE_URL_STORAGE);
if (storedKey) {
const url = (storedUrl || DEFAULT_BASE_URL).replace(/\/$/, "");
setApiKey(storedKey);
setBaseUrl(url);
setClient(() => createClient(storedKey, url));
}
} catch {
} finally {
setIsLoading(false);
}
})();
}, []);
const saveSettings = useCallback(
async (newApiKey: string, newBaseUrl: string) => {
await SecureStore.setItemAsync(API_KEY_STORAGE, newApiKey);
await SecureStore.setItemAsync(BASE_URL_STORAGE, newBaseUrl);
setApiKey(newApiKey);
setBaseUrl(newBaseUrl);
setClient(() => createClient(newApiKey, newBaseUrl));
},
[]
);
const clearSettings = useCallback(async () => {
await SecureStore.deleteItemAsync(API_KEY_STORAGE);
await SecureStore.deleteItemAsync(BASE_URL_STORAGE);
setApiKey("");
setBaseUrl(DEFAULT_BASE_URL);
setClient(null);
}, []);
return (
<PostizContext.Provider
value={{
apiKey,
baseUrl,
isConfigured: !!apiKey,
isLoading,
client,
saveSettings,
clearSettings,
}}
>
{children}
</PostizContext.Provider>
);
}
export function usePostiz() {
return useContext(PostizContext);
}