@oxyhq/services
Version:
Reusable OxyHQ module to handle authentication, user management, karma system, device-based session management and more 🚀
281 lines (236 loc) • 7.91 kB
text/typescript
import { create } from 'zustand';
import { Asset, AssetUploadProgress, AssetLink } from '../../models/interfaces';
interface AssetState {
// Asset data
assets: Record<string, Asset>;
uploadProgress: Record<string, AssetUploadProgress>;
// Loading states
loading: {
uploading: boolean;
linking: boolean;
deleting: boolean;
};
// Error states
errors: {
upload?: string;
link?: string;
delete?: string;
};
// Actions
setAsset: (asset: Asset) => void;
setAssets: (assets: Asset[]) => void;
removeAsset: (assetId: string) => void;
// Upload progress actions
setUploadProgress: (fileId: string, progress: AssetUploadProgress) => void;
removeUploadProgress: (fileId: string) => void;
// Link management
addLink: (assetId: string, link: AssetLink) => void;
removeLink: (assetId: string, app: string, entityType: string, entityId: string) => void;
// Loading states
setUploading: (uploading: boolean) => void;
setLinking: (linking: boolean) => void;
setDeleting: (deleting: boolean) => void;
// Error management
setUploadError: (error?: string) => void;
setLinkError: (error?: string) => void;
setDeleteError: (error?: string) => void;
clearErrors: () => void;
// Utility methods
getAssetsByApp: (app: string) => Asset[];
getAssetsByEntity: (app: string, entityType: string, entityId: string) => Asset[];
getAssetUsageCount: (assetId: string) => number;
isAssetLinked: (assetId: string, app: string, entityType: string, entityId: string) => boolean;
// Reset store
reset: () => void;
}
const initialState = {
assets: {},
uploadProgress: {},
loading: {
uploading: false,
linking: false,
deleting: false,
},
errors: {},
};
export const useAssetStore = create<AssetState>((set, get) => ({
...initialState,
// Asset management
setAsset: (asset: Asset) => {
set((state) => ({
assets: {
...state.assets,
[asset.id]: asset,
},
}));
},
setAssets: (assets: Asset[]) => {
set((state) => {
const assetMap = assets.reduce((acc, asset) => {
acc[asset.id] = asset;
return acc;
}, {} as Record<string, Asset>);
return {
assets: {
...state.assets,
...assetMap,
},
};
});
},
removeAsset: (assetId: string) => {
set((state) => {
const { [assetId]: removed, ...rest } = state.assets;
return { assets: rest };
});
},
// Upload progress
setUploadProgress: (fileId: string, progress: AssetUploadProgress) => {
set((state) => ({
uploadProgress: {
...state.uploadProgress,
[fileId]: progress,
},
}));
},
removeUploadProgress: (fileId: string) => {
set((state) => {
const { [fileId]: removed, ...rest } = state.uploadProgress;
return { uploadProgress: rest };
});
},
// Link management
addLink: (assetId: string, link: AssetLink) => {
set((state) => {
const asset = state.assets[assetId];
if (!asset) return state;
// Check if link already exists
const existingLink = asset.links.find(
(l: AssetLink) => l.app === link.app &&
l.entityType === link.entityType &&
l.entityId === link.entityId
);
if (existingLink) return state;
const updatedAsset = {
...asset,
links: [...asset.links, link],
usageCount: asset.links.length + 1,
};
return {
assets: {
...state.assets,
[assetId]: updatedAsset,
},
};
});
},
removeLink: (assetId: string, app: string, entityType: string, entityId: string) => {
set((state) => {
const asset = state.assets[assetId];
if (!asset) return state;
const filteredLinks = asset.links.filter(
(link: AssetLink) => !(link.app === app &&
link.entityType === entityType &&
link.entityId === entityId)
);
const updatedAsset = {
...asset,
links: filteredLinks,
usageCount: filteredLinks.length,
status: filteredLinks.length === 0 ? 'trash' as const : asset.status,
};
return {
assets: {
...state.assets,
[assetId]: updatedAsset,
},
};
});
},
// Loading states
setUploading: (uploading: boolean) => {
set((state) => ({
loading: { ...state.loading, uploading },
}));
},
setLinking: (linking: boolean) => {
set((state) => ({
loading: { ...state.loading, linking },
}));
},
setDeleting: (deleting: boolean) => {
set((state) => ({
loading: { ...state.loading, deleting },
}));
},
// Error management
setUploadError: (error?: string) => {
set((state) => ({
errors: { ...state.errors, upload: error },
}));
},
setLinkError: (error?: string) => {
set((state) => ({
errors: { ...state.errors, link: error },
}));
},
setDeleteError: (error?: string) => {
set((state) => ({
errors: { ...state.errors, delete: error },
}));
},
clearErrors: () => {
set({ errors: {} });
},
// Utility methods
getAssetsByApp: (app: string) => {
const { assets } = get();
return Object.values(assets).filter((asset) =>
asset.links.some((link: AssetLink) => link.app === app)
);
},
getAssetsByEntity: (app: string, entityType: string, entityId: string) => {
const { assets } = get();
return Object.values(assets).filter((asset) =>
asset.links.some(
(link: AssetLink) => link.app === app &&
link.entityType === entityType &&
link.entityId === entityId
)
);
},
getAssetUsageCount: (assetId: string) => {
const { assets } = get();
const asset = assets[assetId];
return asset ? asset.usageCount : 0;
},
isAssetLinked: (assetId: string, app: string, entityType: string, entityId: string) => {
const { assets } = get();
const asset = assets[assetId];
if (!asset) return false;
return asset.links.some(
(link: AssetLink) => link.app === app &&
link.entityType === entityType &&
link.entityId === entityId
);
},
// Reset store
reset: () => {
set(initialState);
},
}));
// Selector hooks for convenience
export const useAssets = () => useAssetStore((state) => Object.values(state.assets));
export const useAsset = (assetId: string) => useAssetStore((state) => state.assets[assetId]);
export const useUploadProgress = () => useAssetStore((state) => state.uploadProgress);
export const useAssetLoading = () => useAssetStore((state) => state.loading);
export const useAssetErrors = () => useAssetStore((state) => state.errors);
// Typed selectors for specific use cases
export const useAssetsByApp = (app: string) =>
useAssetStore((state) => state.getAssetsByApp(app));
export const useAssetsByEntity = (app: string, entityType: string, entityId: string) =>
useAssetStore((state) => state.getAssetsByEntity(app, entityType, entityId));
export const useAssetUsageCount = (assetId: string) =>
useAssetStore((state) => state.getAssetUsageCount(assetId));
export const useIsAssetLinked = (assetId: string, app: string, entityType: string, entityId: string) =>
useAssetStore((state) => state.isAssetLinked(assetId, app, entityType, entityId));