@replyke/core
Version:
Replyke: Build interactive apps with social features like comments, votes, feeds, user lists, notifications, and more.
313 lines • 13.7 kB
JavaScript
;
var _a;
Object.defineProperty(exports, "__esModule", { value: true });
exports.selectEntityListConfig = exports.selectEntityListFilters = exports.selectEntityListSort = exports.selectEntityListHasMore = exports.selectEntityListLoading = exports.selectEntityListEntities = exports.selectEntityList = exports.cleanupOldLists = exports.cleanupList = exports.updateKeywordsFilters = exports.removeEntity = exports.addEntity = exports.setEntityListError = exports.setEntityListHasMore = exports.incrementPage = exports.setEntityListEntities = exports.setEntityListLoading = exports.updateFiltersAndSortConfig = exports.initializeList = exports.entityListsSlice = void 0;
const toolkit_1 = require("@reduxjs/toolkit");
// Default state for a new entity list
const createDefaultEntityListState = () => ({
entities: [],
page: 1,
loading: true,
hasMore: true,
error: null,
lastFetched: null,
// Default configuration
sourceId: null,
spaceId: null,
limit: 10,
include: null,
// Default filters (user-controlled only)
sortBy: "hot",
sortByReaction: "upvote",
sortDir: null,
sortType: "auto",
timeFrame: null,
userId: null,
followedOnly: false,
keywordsFilters: null,
titleFilters: null,
contentFilters: null,
attachmentsFilters: null,
locationFilters: null,
metadataFilters: null,
});
// Initial state
const initialState = {
lists: {},
};
// Create the slice
exports.entityListsSlice = (0, toolkit_1.createSlice)({
name: "entityLists",
initialState,
reducers: {
// Initialize or get existing list
initializeList: (state, action) => {
const { listId } = action.payload;
if (!state.lists[listId]) {
state.lists[listId] = createDefaultEntityListState();
}
},
// Update filters and sort configuration
updateFiltersAndSortConfig: (state, action) => {
const { listId, filters, sort, config, options } = action.payload;
// Ensure list exists
if (!state.lists[listId]) {
state.lists[listId] = createDefaultEntityListState();
}
const list = state.lists[listId];
// Handle resetFilters flag - reset only filter properties
if (options?.resetFilters) {
const defaultState = createDefaultEntityListState();
list.timeFrame = defaultState.timeFrame;
list.userId = defaultState.userId;
list.followedOnly = defaultState.followedOnly;
list.keywordsFilters = defaultState.keywordsFilters;
list.titleFilters = defaultState.titleFilters;
list.contentFilters = defaultState.contentFilters;
list.attachmentsFilters = defaultState.attachmentsFilters;
list.locationFilters = defaultState.locationFilters;
list.metadataFilters = defaultState.metadataFilters;
}
// Handle resetSort flag - reset only sort properties
if (options?.resetSort) {
const defaultState = createDefaultEntityListState();
list.sortBy = defaultState.sortBy;
list.sortByReaction = defaultState.sortByReaction;
list.sortDir = defaultState.sortDir;
list.sortType = defaultState.sortType;
}
// Apply specified filters
Object.keys(filters).forEach((key) => {
if (filters[key] !== undefined) {
list[key] = filters[key];
}
});
// Apply specified sort configuration
if (sort) {
if (sort.sortBy !== undefined)
list.sortBy = sort.sortBy;
if (sort.sortByReaction !== undefined)
list.sortByReaction = sort.sortByReaction;
if (sort.sortDir !== undefined)
list.sortDir = sort.sortDir;
if (sort.sortType !== undefined)
list.sortType = sort.sortType;
}
// Update config if provided
if (config) {
if (config.sourceId !== undefined) {
list.sourceId = config.sourceId;
}
if (config.spaceId !== undefined) {
list.spaceId = config.spaceId;
}
if (config.limit !== undefined) {
list.limit = config.limit;
}
if (config.include !== undefined) {
list.include = config.include;
}
}
// Reset pagination when filters or sort changes
list.page = 1;
list.hasMore = true;
list.error = null;
},
// Set loading state for entity list
setEntityListLoading: (state, action) => {
const { listId, loading } = action.payload;
if (state.lists[listId]) {
state.lists[listId].loading = loading;
}
},
// Set entities for entity list
setEntityListEntities: (state, action) => {
const { listId, entities, append = false } = action.payload;
if (!state.lists[listId]) {
state.lists[listId] = createDefaultEntityListState();
}
const list = state.lists[listId];
if (append) {
// Filter out duplicates when appending
const existingIds = new Set(list.entities.map((e) => e.id));
const newEntities = entities.filter((e) => !existingIds.has(e.id));
list.entities = [...list.entities, ...newEntities];
}
else {
list.entities = entities;
}
list.loading = false;
list.lastFetched = Date.now();
// Note: hasMore is set explicitly by the caller based on limit from hook props
},
// Increment page for load more
incrementPage: (state, action) => {
const listId = action.payload;
if (state.lists[listId]) {
state.lists[listId].page += 1;
}
},
// Set hasMore for entity list
setEntityListHasMore: (state, action) => {
const { listId, hasMore } = action.payload;
if (state.lists[listId]) {
state.lists[listId].hasMore = hasMore;
}
},
// Set error for entity list
setEntityListError: (state, action) => {
const { listId, error } = action.payload;
if (state.lists[listId]) {
state.lists[listId].error = error;
state.lists[listId].loading = false;
}
},
// Add entity
addEntity: (state, action) => {
const { listId, entity, insertPosition = "first" } = action.payload;
if (!state.lists[listId])
return;
const list = state.lists[listId];
if (insertPosition === "last") {
list.entities.push(entity);
}
else {
list.entities.unshift(entity);
}
},
// Remove entity
removeEntity: (state, action) => {
const { listId, entityId } = action.payload;
if (!state.lists[listId])
return;
const list = state.lists[listId];
list.entities = list.entities.filter((e) => e.id !== entityId);
},
// Update keywords filters (special case due to complexity)
updateKeywordsFilters: (state, action) => {
const { listId, type, key, value } = action.payload;
if (!state.lists[listId]) {
state.lists[listId] = createDefaultEntityListState();
}
const list = state.lists[listId];
const items = Array.isArray(value) ? value : value ? [value] : [];
let newFilters = list.keywordsFilters || {};
switch (type) {
case "add": {
if (key === "both")
break; // Invalid to add to both
newFilters = {
...newFilters,
[key]: Array.from(new Set([...(newFilters[key] || []), ...items])),
};
break;
}
case "remove": {
if (key === "both") {
newFilters = {
includes: (newFilters.includes || []).filter((item) => !items.includes(item)),
doesNotInclude: (newFilters.doesNotInclude || []).filter((item) => !items.includes(item)),
};
}
else {
newFilters = {
...newFilters,
[key]: (newFilters[key] || []).filter((item) => !items.includes(item)),
};
}
break;
}
case "reset": {
if (key === "both") {
newFilters = {};
}
else {
newFilters = {
...newFilters,
[key]: undefined,
};
}
break;
}
case "replace": {
if (key === "both")
break; // Replace does not apply to both
newFilters = {
...newFilters,
[key]: items,
};
break;
}
}
list.keywordsFilters =
Object.keys(newFilters).length > 0 ? newFilters : null;
// Reset pagination when filters change
list.page = 1;
list.hasMore = true;
list.error = null;
},
// Clean up unused lists (for memory management)
cleanupList: (state, action) => {
const listId = action.payload;
delete state.lists[listId];
},
// Clean up old lists (older than TTL)
cleanupOldLists: (state, action) => {
const ttl = action.payload; // TTL in milliseconds
const now = Date.now();
Object.keys(state.lists).forEach((listId) => {
const list = state.lists[listId];
if (list.lastFetched && now - list.lastFetched > ttl) {
delete state.lists[listId];
}
});
},
},
});
// Export actions
_a = exports.entityListsSlice.actions, exports.initializeList = _a.initializeList, exports.updateFiltersAndSortConfig = _a.updateFiltersAndSortConfig, exports.setEntityListLoading = _a.setEntityListLoading, exports.setEntityListEntities = _a.setEntityListEntities, exports.incrementPage = _a.incrementPage, exports.setEntityListHasMore = _a.setEntityListHasMore, exports.setEntityListError = _a.setEntityListError, exports.addEntity = _a.addEntity, exports.removeEntity = _a.removeEntity, exports.updateKeywordsFilters = _a.updateKeywordsFilters, exports.cleanupList = _a.cleanupList, exports.cleanupOldLists = _a.cleanupOldLists;
// Base selectors - use namespaced state for dual-mode support
const selectEntityListsState = (state) => state.replyke.entityLists;
const selectListId = (_, listId) => listId;
// Memoized selectors using createSelector
exports.selectEntityList = (0, toolkit_1.createSelector)([selectEntityListsState, selectListId], (entityListsState, listId) => entityListsState.lists[listId]);
exports.selectEntityListEntities = (0, toolkit_1.createSelector)([exports.selectEntityList], (entityList) => entityList?.entities || []);
exports.selectEntityListLoading = (0, toolkit_1.createSelector)([exports.selectEntityList], (entityList) => entityList?.loading || false);
exports.selectEntityListHasMore = (0, toolkit_1.createSelector)([exports.selectEntityList], (entityList) => entityList?.hasMore || false);
exports.selectEntityListSort = (0, toolkit_1.createSelector)([exports.selectEntityList], (entityList) => {
if (!entityList)
return null;
return {
sortBy: entityList.sortBy,
sortByReaction: entityList.sortByReaction,
sortDir: entityList.sortDir,
sortType: entityList.sortType,
};
});
exports.selectEntityListFilters = (0, toolkit_1.createSelector)([exports.selectEntityList], (entityList) => {
if (!entityList)
return null;
return {
timeFrame: entityList.timeFrame,
userId: entityList.userId,
followedOnly: entityList.followedOnly,
keywordsFilters: entityList.keywordsFilters,
titleFilters: entityList.titleFilters,
contentFilters: entityList.contentFilters,
attachmentsFilters: entityList.attachmentsFilters,
locationFilters: entityList.locationFilters,
metadataFilters: entityList.metadataFilters,
};
});
exports.selectEntityListConfig = (0, toolkit_1.createSelector)([exports.selectEntityList], (entityList) => {
if (!entityList)
return null;
return {
sourceId: entityList.sourceId,
spaceId: entityList.spaceId,
limit: entityList.limit,
include: entityList.include,
};
});
exports.default = exports.entityListsSlice.reducer;
//# sourceMappingURL=entityListsSlice.js.map