@oxyhq/services
Version:
Reusable OxyHQ module to handle authentication, user management, karma system, device-based session management and more 🚀
145 lines (141 loc) • 3.74 kB
JavaScript
;
import { create } from 'zustand';
// Shallow compare two file metadata objects by keys/values
function shallowEqualFile(a, b) {
if (a === b) return true;
if (!a || !b) return false;
const aKeys = Object.keys(a);
const bKeys = Object.keys(b);
if (aKeys.length !== bKeys.length) return false;
for (const k of aKeys) {
// treat metadata/variants shallowly by reference
if (a[k] !== b[k]) return false;
}
return true;
}
// Basic upload progress type for aggregate tracking
const initialState = {
files: {},
order: [],
uploading: false,
deleting: null,
uploadProgress: null
};
export const useFileStore = create((set, get) => ({
...initialState,
setFiles: (files, opts) => set(state => {
const merge = opts?.merge !== false; // default true
if (!merge) {
const map = {};
const order = [];
files.forEach(f => {
map[f.id] = f;
order.push(f.id);
});
// detect if identical to avoid redundant updates
const sameOrder = order.length === state.order.length && order.every((id, i) => id === state.order[i]);
let sameFiles = sameOrder;
if (sameOrder) {
sameFiles = order.every(id => state.files[id] && shallowEqualFile(state.files[id], map[id]));
}
if (sameOrder && sameFiles) return {};
return {
files: map,
order
};
}
const newFiles = {
...state.files
};
const newOrder = [...state.order];
let changed = false;
files.forEach(f => {
const prev = state.files[f.id];
const merged = {
...(prev || {}),
...f
};
if (!prev || !shallowEqualFile(prev, merged)) {
newFiles[f.id] = merged;
changed = true;
}
if (!newOrder.includes(f.id)) {
newOrder.unshift(f.id);
changed = true;
}
});
if (!changed) return {};
return {
files: newFiles,
order: newOrder
};
}),
addFile: (file, opts) => set(state => {
const prepend = opts?.prepend !== false; // default true
if (state.files[file.id]) {
if (shallowEqualFile(state.files[file.id], file)) return {};
return {
files: {
...state.files,
[file.id]: file
}
};
}
return {
files: {
...state.files,
[file.id]: file
},
order: prepend ? [file.id, ...state.order] : [...state.order, file.id]
};
}),
updateFile: (id, patch) => set(state => {
const existing = state.files[id];
if (!existing) return {};
const updated = {
...existing,
...patch
};
if (shallowEqualFile(existing, updated)) return {};
return {
files: {
...state.files,
[id]: updated
}
};
}),
removeFile: id => set(state => {
if (!state.files[id]) return {};
const {
[id]: _removed,
...rest
} = state.files;
const newOrder = state.order.filter(fid => fid !== id);
return {
files: rest,
order: newOrder
};
}),
setUploading: val => set({
uploading: val
}),
setDeleting: id => set({
deleting: id
}),
setUploadProgress: p => set({
uploadProgress: p
}),
reset: () => set(initialState)
}));
// selectors
export const useFiles = () => {
const files = useFileStore(s => s.files);
const order = useFileStore(s => s.order);
// Return stable array when contents unchanged
const out = order.map(id => files[id]);
return out;
};
export const useUploading = () => useFileStore(s => s.uploading);
export const useUploadAggregateProgress = () => useFileStore(s => s.uploadProgress);
export const useDeleting = () => useFileStore(s => s.deleting);
//# sourceMappingURL=fileStore.js.map