App state: refactored reducers and actions for apps, categories and bookmarks
This commit is contained in:
parent
7e89ab0204
commit
adc017c48d
10 changed files with 893 additions and 1 deletions
170
client/src/store/action-creators/app.ts
Normal file
170
client/src/store/action-creators/app.ts
Normal file
|
@ -0,0 +1,170 @@
|
|||
import { ActionType } from '../action-types';
|
||||
import { Dispatch } from 'redux';
|
||||
import { ApiResponse, App, Config, NewApp } from '../../interfaces';
|
||||
import {
|
||||
AddAppAction,
|
||||
DeleteAppAction,
|
||||
GetAppsAction,
|
||||
PinAppAction,
|
||||
ReorderAppsAction,
|
||||
SortAppsAction,
|
||||
UpdateAppAction,
|
||||
} from '../actions/app';
|
||||
import axios from 'axios';
|
||||
import { createNotification } from '.';
|
||||
|
||||
export const getApps =
|
||||
() => async (dispatch: Dispatch<GetAppsAction<undefined | App[]>>) => {
|
||||
dispatch({
|
||||
type: ActionType.getApps,
|
||||
payload: undefined,
|
||||
});
|
||||
|
||||
try {
|
||||
const res = await axios.get<ApiResponse<App[]>>('/api/apps');
|
||||
|
||||
dispatch({
|
||||
type: ActionType.getAppsSuccess,
|
||||
payload: res.data.data,
|
||||
});
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
};
|
||||
|
||||
export const pinApp =
|
||||
(app: App) => async (dispatch: Dispatch<PinAppAction>) => {
|
||||
try {
|
||||
const { id, isPinned, name } = app;
|
||||
const res = await axios.put<ApiResponse<App>>(`/api/apps/${id}`, {
|
||||
isPinned: !isPinned,
|
||||
});
|
||||
|
||||
const status = isPinned
|
||||
? 'unpinned from Homescreen'
|
||||
: 'pinned to Homescreen';
|
||||
|
||||
createNotification({
|
||||
title: 'Success',
|
||||
message: `App ${name} ${status}`,
|
||||
});
|
||||
|
||||
dispatch({
|
||||
type: ActionType.pinApp,
|
||||
payload: res.data.data,
|
||||
});
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
};
|
||||
|
||||
export const addApp =
|
||||
(formData: NewApp | FormData) => async (dispatch: Dispatch<AddAppAction>) => {
|
||||
try {
|
||||
const res = await axios.post<ApiResponse<App>>('/api/apps', formData);
|
||||
|
||||
createNotification({
|
||||
title: 'Success',
|
||||
message: `App added`,
|
||||
});
|
||||
|
||||
await dispatch({
|
||||
type: ActionType.addAppSuccess,
|
||||
payload: res.data.data,
|
||||
});
|
||||
|
||||
// Sort apps
|
||||
// dispatch<any>(sortApps());
|
||||
sortApps();
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
};
|
||||
|
||||
export const deleteApp =
|
||||
(id: number) => async (dispatch: Dispatch<DeleteAppAction>) => {
|
||||
try {
|
||||
await axios.delete<ApiResponse<{}>>(`/api/apps/${id}`);
|
||||
|
||||
createNotification({
|
||||
title: 'Success',
|
||||
message: 'App deleted',
|
||||
});
|
||||
|
||||
dispatch({
|
||||
type: ActionType.deleteApp,
|
||||
payload: id,
|
||||
});
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
};
|
||||
|
||||
export const updateApp =
|
||||
(id: number, formData: NewApp | FormData) =>
|
||||
async (dispatch: Dispatch<UpdateAppAction>) => {
|
||||
try {
|
||||
const res = await axios.put<ApiResponse<App>>(
|
||||
`/api/apps/${id}`,
|
||||
formData
|
||||
);
|
||||
|
||||
createNotification({
|
||||
title: 'Success',
|
||||
message: `App updated`,
|
||||
});
|
||||
|
||||
await dispatch({
|
||||
type: ActionType.updateApp,
|
||||
payload: res.data.data,
|
||||
});
|
||||
|
||||
// Sort apps
|
||||
dispatch<any>(sortApps());
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
};
|
||||
|
||||
export const reorderApps =
|
||||
(apps: App[]) => async (dispatch: Dispatch<ReorderAppsAction>) => {
|
||||
interface ReorderQuery {
|
||||
apps: {
|
||||
id: number;
|
||||
orderId: number;
|
||||
}[];
|
||||
}
|
||||
|
||||
try {
|
||||
const updateQuery: ReorderQuery = { apps: [] };
|
||||
|
||||
apps.forEach((app, index) =>
|
||||
updateQuery.apps.push({
|
||||
id: app.id,
|
||||
orderId: index + 1,
|
||||
})
|
||||
);
|
||||
|
||||
await axios.put<ApiResponse<{}>>('/api/apps/0/reorder', updateQuery);
|
||||
|
||||
dispatch({
|
||||
type: ActionType.reorderApps,
|
||||
payload: apps,
|
||||
});
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
};
|
||||
|
||||
export const sortApps = () => async (dispatch: Dispatch<SortAppsAction>) => {
|
||||
try {
|
||||
const res = await axios.get<ApiResponse<Config>>('/api/config');
|
||||
|
||||
dispatch({
|
||||
type: ActionType.sortApps,
|
||||
payload: res.data.data.useOrdering,
|
||||
});
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
};
|
289
client/src/store/action-creators/bookmark.ts
Normal file
289
client/src/store/action-creators/bookmark.ts
Normal file
|
@ -0,0 +1,289 @@
|
|||
import axios from 'axios';
|
||||
import { Dispatch } from 'redux';
|
||||
import { createNotification } from '.';
|
||||
import {
|
||||
ApiResponse,
|
||||
Bookmark,
|
||||
Category,
|
||||
Config,
|
||||
NewBookmark,
|
||||
NewCategory,
|
||||
} from '../../interfaces';
|
||||
import { ActionType } from '../action-types';
|
||||
import {
|
||||
AddBookmarkAction,
|
||||
AddCategoryAction,
|
||||
DeleteBookmarkAction,
|
||||
DeleteCategoryAction,
|
||||
GetCategoriesAction,
|
||||
PinCategoryAction,
|
||||
ReorderCategoriesAction,
|
||||
SortCategoriesAction,
|
||||
UpdateBookmarkAction,
|
||||
UpdateCategoryAction,
|
||||
} from '../actions/bookmark';
|
||||
|
||||
export const getCategories =
|
||||
() =>
|
||||
async (dispatch: Dispatch<GetCategoriesAction<undefined | Category[]>>) => {
|
||||
dispatch({
|
||||
type: ActionType.getCategories,
|
||||
payload: undefined,
|
||||
});
|
||||
|
||||
try {
|
||||
const res = await axios.get<ApiResponse<Category[]>>('/api/categories');
|
||||
|
||||
dispatch({
|
||||
type: ActionType.getCategoriesSuccess,
|
||||
payload: res.data.data,
|
||||
});
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
};
|
||||
|
||||
export const addCategory =
|
||||
(formData: NewCategory) => async (dispatch: Dispatch<AddCategoryAction>) => {
|
||||
try {
|
||||
const res = await axios.post<ApiResponse<Category>>(
|
||||
'/api/categories',
|
||||
formData
|
||||
);
|
||||
|
||||
createNotification({
|
||||
title: 'Success',
|
||||
message: `Category ${formData.name} created`,
|
||||
});
|
||||
|
||||
dispatch({
|
||||
type: ActionType.addCategory,
|
||||
payload: res.data.data,
|
||||
});
|
||||
|
||||
// dispatch<any>(sortCategories());
|
||||
sortCategories();
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
};
|
||||
|
||||
export const addBookmark =
|
||||
(formData: NewBookmark | FormData) =>
|
||||
async (dispatch: Dispatch<AddBookmarkAction>) => {
|
||||
try {
|
||||
const res = await axios.post<ApiResponse<Bookmark>>(
|
||||
'/api/bookmarks',
|
||||
formData
|
||||
);
|
||||
|
||||
createNotification({
|
||||
title: 'Success',
|
||||
message: `Bookmark created`,
|
||||
});
|
||||
|
||||
dispatch({
|
||||
type: ActionType.addBookmark,
|
||||
payload: res.data.data,
|
||||
});
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
};
|
||||
|
||||
export const pinCategory =
|
||||
(category: Category) => async (dispatch: Dispatch<PinCategoryAction>) => {
|
||||
try {
|
||||
const { id, isPinned, name } = category;
|
||||
const res = await axios.put<ApiResponse<Category>>(
|
||||
`/api/categories/${id}`,
|
||||
{ isPinned: !isPinned }
|
||||
);
|
||||
|
||||
const status = isPinned
|
||||
? 'unpinned from Homescreen'
|
||||
: 'pinned to Homescreen';
|
||||
|
||||
createNotification({
|
||||
title: 'Success',
|
||||
message: `Category ${name} ${status}`,
|
||||
});
|
||||
|
||||
dispatch({
|
||||
type: ActionType.pinCategory,
|
||||
payload: res.data.data,
|
||||
});
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
};
|
||||
|
||||
export const deleteCategory =
|
||||
(id: number) => async (dispatch: Dispatch<DeleteCategoryAction>) => {
|
||||
try {
|
||||
await axios.delete<ApiResponse<{}>>(`/api/categories/${id}`);
|
||||
|
||||
createNotification({
|
||||
title: 'Success',
|
||||
message: `Category deleted`,
|
||||
});
|
||||
|
||||
dispatch({
|
||||
type: ActionType.deleteCategory,
|
||||
payload: id,
|
||||
});
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
};
|
||||
|
||||
export const updateCategory =
|
||||
(id: number, formData: NewCategory) =>
|
||||
async (dispatch: Dispatch<UpdateCategoryAction>) => {
|
||||
try {
|
||||
const res = await axios.put<ApiResponse<Category>>(
|
||||
`/api/categories/${id}`,
|
||||
formData
|
||||
);
|
||||
createNotification({
|
||||
title: 'Success',
|
||||
message: `Category ${formData.name} updated`,
|
||||
});
|
||||
|
||||
dispatch({
|
||||
type: ActionType.updateCategory,
|
||||
payload: res.data.data,
|
||||
});
|
||||
|
||||
// dispatch<any>(sortCategories());
|
||||
sortCategories();
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
};
|
||||
|
||||
export const deleteBookmark =
|
||||
(bookmarkId: number, categoryId: number) =>
|
||||
async (dispatch: Dispatch<DeleteBookmarkAction>) => {
|
||||
try {
|
||||
await axios.delete<ApiResponse<{}>>(`/api/bookmarks/${bookmarkId}`);
|
||||
|
||||
createNotification({
|
||||
title: 'Success',
|
||||
message: 'Bookmark deleted',
|
||||
});
|
||||
|
||||
dispatch({
|
||||
type: ActionType.deleteBookmark,
|
||||
payload: {
|
||||
bookmarkId,
|
||||
categoryId,
|
||||
},
|
||||
});
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
};
|
||||
|
||||
export const updateBookmark =
|
||||
(
|
||||
bookmarkId: number,
|
||||
formData: NewBookmark | FormData,
|
||||
category: {
|
||||
prev: number;
|
||||
curr: number;
|
||||
}
|
||||
) =>
|
||||
async (
|
||||
dispatch: Dispatch<
|
||||
DeleteBookmarkAction | AddBookmarkAction | UpdateBookmarkAction
|
||||
>
|
||||
) => {
|
||||
try {
|
||||
const res = await axios.put<ApiResponse<Bookmark>>(
|
||||
`/api/bookmarks/${bookmarkId}`,
|
||||
formData
|
||||
);
|
||||
|
||||
createNotification({
|
||||
title: 'Success',
|
||||
message: `Bookmark updated`,
|
||||
});
|
||||
|
||||
// Check if category was changed
|
||||
const categoryWasChanged = category.curr !== category.prev;
|
||||
|
||||
if (categoryWasChanged) {
|
||||
// Delete bookmark from old category
|
||||
dispatch({
|
||||
type: ActionType.deleteBookmark,
|
||||
payload: {
|
||||
bookmarkId,
|
||||
categoryId: category.prev,
|
||||
},
|
||||
});
|
||||
|
||||
// Add bookmark to the new category
|
||||
dispatch({
|
||||
type: ActionType.addBookmark,
|
||||
payload: res.data.data,
|
||||
});
|
||||
} else {
|
||||
// Else update only name/url/icon
|
||||
dispatch({
|
||||
type: ActionType.updateBookmark,
|
||||
payload: res.data.data,
|
||||
});
|
||||
}
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
};
|
||||
|
||||
export const sortCategories =
|
||||
() => async (dispatch: Dispatch<SortCategoriesAction>) => {
|
||||
try {
|
||||
const res = await axios.get<ApiResponse<Config>>('/api/config');
|
||||
|
||||
dispatch({
|
||||
type: ActionType.sortCategories,
|
||||
payload: res.data.data.useOrdering,
|
||||
});
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
};
|
||||
|
||||
export const reorderCategories =
|
||||
(categories: Category[]) =>
|
||||
async (dispatch: Dispatch<ReorderCategoriesAction>) => {
|
||||
interface ReorderQuery {
|
||||
categories: {
|
||||
id: number;
|
||||
orderId: number;
|
||||
}[];
|
||||
}
|
||||
|
||||
try {
|
||||
const updateQuery: ReorderQuery = { categories: [] };
|
||||
|
||||
categories.forEach((category, index) =>
|
||||
updateQuery.categories.push({
|
||||
id: category.id,
|
||||
orderId: index + 1,
|
||||
})
|
||||
);
|
||||
|
||||
await axios.put<ApiResponse<{}>>(
|
||||
'/api/categories/0/reorder',
|
||||
updateQuery
|
||||
);
|
||||
|
||||
dispatch({
|
||||
type: ActionType.reorderCategories,
|
||||
payload: categories,
|
||||
});
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
};
|
|
@ -1,3 +1,5 @@
|
|||
export * from './theme';
|
||||
export * from './config';
|
||||
export * from './notification';
|
||||
export * from './app';
|
||||
export * from './bookmark';
|
||||
|
|
|
@ -4,6 +4,7 @@ export enum ActionType {
|
|||
// CONFIG
|
||||
getConfig = 'GET_CONFIG',
|
||||
updateConfig = 'UPDATE_CONFIG',
|
||||
// QUERIES
|
||||
addQuery = 'ADD_QUERY',
|
||||
deleteQuery = 'DELETE_QUERY',
|
||||
fetchQueries = 'FETCH_QUERIES',
|
||||
|
@ -11,4 +12,29 @@ export enum ActionType {
|
|||
// NOTIFICATIONS
|
||||
createNotification = 'CREATE_NOTIFICATION',
|
||||
clearNotification = 'CLEAR_NOTIFICATION',
|
||||
// APPS
|
||||
getApps = 'GET_APPS',
|
||||
getAppsSuccess = 'GET_APPS_SUCCESS',
|
||||
getAppsError = 'GET_APPS_ERROR',
|
||||
pinApp = 'PIN_APP',
|
||||
addApp = 'ADD_APP',
|
||||
addAppSuccess = 'ADD_APP_SUCCESS',
|
||||
deleteApp = 'DELETE_APP',
|
||||
updateApp = 'UPDATE_APP',
|
||||
reorderApps = 'REORDER_APPS',
|
||||
sortApps = 'SORT_APPS',
|
||||
// CATEGORES
|
||||
getCategories = 'GET_CATEGORIES',
|
||||
getCategoriesSuccess = 'GET_CATEGORIES_SUCCESS',
|
||||
getCategoriesError = 'GET_CATEGORIES_ERROR',
|
||||
addCategory = 'ADD_CATEGORY',
|
||||
pinCategory = 'PIN_CATEGORY',
|
||||
deleteCategory = 'DELETE_CATEGORY',
|
||||
updateCategory = 'UPDATE_CATEGORY',
|
||||
sortCategories = 'SORT_CATEGORIES',
|
||||
reorderCategories = 'REORDER_CATEGORIES',
|
||||
// BOOKMARKS
|
||||
addBookmark = 'ADD_BOOKMARK',
|
||||
deleteBookmark = 'DELETE_BOOKMARK',
|
||||
updateBookmark = 'UPDATE_BOOKMARK',
|
||||
}
|
||||
|
|
38
client/src/store/actions/app.ts
Normal file
38
client/src/store/actions/app.ts
Normal file
|
@ -0,0 +1,38 @@
|
|||
import { ActionType } from '../action-types';
|
||||
import { App } from '../../interfaces';
|
||||
|
||||
export interface GetAppsAction<T> {
|
||||
type:
|
||||
| ActionType.getApps
|
||||
| ActionType.getAppsSuccess
|
||||
| ActionType.getAppsError;
|
||||
payload: T;
|
||||
}
|
||||
export interface PinAppAction {
|
||||
type: ActionType.pinApp;
|
||||
payload: App;
|
||||
}
|
||||
|
||||
export interface AddAppAction {
|
||||
type: ActionType.addAppSuccess;
|
||||
payload: App;
|
||||
}
|
||||
export interface DeleteAppAction {
|
||||
type: ActionType.deleteApp;
|
||||
payload: number;
|
||||
}
|
||||
|
||||
export interface UpdateAppAction {
|
||||
type: ActionType.updateApp;
|
||||
payload: App;
|
||||
}
|
||||
|
||||
export interface ReorderAppsAction {
|
||||
type: ActionType.reorderApps;
|
||||
payload: App[];
|
||||
}
|
||||
|
||||
export interface SortAppsAction {
|
||||
type: ActionType.sortApps;
|
||||
payload: string;
|
||||
}
|
58
client/src/store/actions/bookmark.ts
Normal file
58
client/src/store/actions/bookmark.ts
Normal file
|
@ -0,0 +1,58 @@
|
|||
import { Bookmark, Category } from '../../interfaces';
|
||||
import { ActionType } from '../action-types';
|
||||
|
||||
export interface GetCategoriesAction<T> {
|
||||
type:
|
||||
| ActionType.getCategories
|
||||
| ActionType.getCategoriesSuccess
|
||||
| ActionType.getCategoriesError;
|
||||
payload: T;
|
||||
}
|
||||
|
||||
export interface AddCategoryAction {
|
||||
type: ActionType.addCategory;
|
||||
payload: Category;
|
||||
}
|
||||
|
||||
export interface AddBookmarkAction {
|
||||
type: ActionType.addBookmark;
|
||||
payload: Bookmark;
|
||||
}
|
||||
|
||||
export interface PinCategoryAction {
|
||||
type: ActionType.pinCategory;
|
||||
payload: Category;
|
||||
}
|
||||
|
||||
export interface DeleteCategoryAction {
|
||||
type: ActionType.deleteCategory;
|
||||
payload: number;
|
||||
}
|
||||
|
||||
export interface UpdateCategoryAction {
|
||||
type: ActionType.updateCategory;
|
||||
payload: Category;
|
||||
}
|
||||
|
||||
export interface DeleteBookmarkAction {
|
||||
type: ActionType.deleteBookmark;
|
||||
payload: {
|
||||
bookmarkId: number;
|
||||
categoryId: number;
|
||||
};
|
||||
}
|
||||
|
||||
export interface UpdateBookmarkAction {
|
||||
type: ActionType.updateBookmark;
|
||||
payload: Bookmark;
|
||||
}
|
||||
|
||||
export interface SortCategoriesAction {
|
||||
type: ActionType.sortCategories;
|
||||
payload: string;
|
||||
}
|
||||
|
||||
export interface ReorderCategoriesAction {
|
||||
type: ActionType.reorderCategories;
|
||||
payload: Category[];
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
import { SetThemeAction } from './theme';
|
||||
|
||||
import {
|
||||
AddQueryAction,
|
||||
DeleteQueryAction,
|
||||
|
@ -7,11 +8,37 @@ import {
|
|||
UpdateConfigAction,
|
||||
UpdateQueryAction,
|
||||
} from './config';
|
||||
|
||||
import {
|
||||
ClearNotificationAction,
|
||||
CreateNotificationAction,
|
||||
} from './notification';
|
||||
|
||||
import {
|
||||
GetAppsAction,
|
||||
PinAppAction,
|
||||
AddAppAction,
|
||||
DeleteAppAction,
|
||||
UpdateAppAction,
|
||||
ReorderAppsAction,
|
||||
SortAppsAction,
|
||||
} from './app';
|
||||
|
||||
import { App } from '../../interfaces';
|
||||
|
||||
import {
|
||||
GetCategoriesAction,
|
||||
AddCategoryAction,
|
||||
PinCategoryAction,
|
||||
DeleteCategoryAction,
|
||||
UpdateCategoryAction,
|
||||
SortCategoriesAction,
|
||||
ReorderCategoriesAction,
|
||||
AddBookmarkAction,
|
||||
DeleteBookmarkAction,
|
||||
UpdateBookmarkAction,
|
||||
} from './bookmark';
|
||||
|
||||
export type Action =
|
||||
// Theme
|
||||
| SetThemeAction
|
||||
|
@ -24,4 +51,24 @@ export type Action =
|
|||
| UpdateQueryAction
|
||||
// Notifications
|
||||
| CreateNotificationAction
|
||||
| ClearNotificationAction;
|
||||
| ClearNotificationAction
|
||||
// Apps
|
||||
| GetAppsAction<undefined | App[]>
|
||||
| PinAppAction
|
||||
| AddAppAction
|
||||
| DeleteAppAction
|
||||
| UpdateAppAction
|
||||
| ReorderAppsAction
|
||||
| SortAppsAction
|
||||
// Categories
|
||||
| GetCategoriesAction<any>
|
||||
| AddCategoryAction
|
||||
| PinCategoryAction
|
||||
| DeleteCategoryAction
|
||||
| UpdateCategoryAction
|
||||
| SortCategoriesAction
|
||||
| ReorderCategoriesAction
|
||||
// Bookmarks
|
||||
| AddBookmarkAction
|
||||
| DeleteBookmarkAction
|
||||
| UpdateBookmarkAction;
|
||||
|
|
92
client/src/store/reducers/app.ts
Normal file
92
client/src/store/reducers/app.ts
Normal file
|
@ -0,0 +1,92 @@
|
|||
import { ActionType } from '../action-types';
|
||||
import { Action } from '../actions/index';
|
||||
import { App } from '../../interfaces';
|
||||
import { sortData } from '../../utility';
|
||||
|
||||
interface AppsState {
|
||||
loading: boolean;
|
||||
apps: App[];
|
||||
errors: string | undefined;
|
||||
}
|
||||
|
||||
const initialState: AppsState = {
|
||||
loading: true,
|
||||
apps: [],
|
||||
errors: undefined,
|
||||
};
|
||||
|
||||
export const appsReducer = (
|
||||
state: AppsState = initialState,
|
||||
action: Action
|
||||
): AppsState => {
|
||||
switch (action.type) {
|
||||
case ActionType.getApps:
|
||||
return {
|
||||
...state,
|
||||
loading: true,
|
||||
errors: undefined,
|
||||
};
|
||||
|
||||
case ActionType.getAppsSuccess:
|
||||
return {
|
||||
...state,
|
||||
loading: false,
|
||||
apps: action.payload || [],
|
||||
};
|
||||
|
||||
case ActionType.pinApp:
|
||||
const pinnedAppIdx = state.apps.findIndex(
|
||||
(app) => app.id === action.payload.id
|
||||
);
|
||||
|
||||
return {
|
||||
...state,
|
||||
apps: [
|
||||
...state.apps.slice(0, pinnedAppIdx),
|
||||
action.payload,
|
||||
...state.apps.slice(pinnedAppIdx + 1),
|
||||
],
|
||||
};
|
||||
|
||||
case ActionType.addAppSuccess:
|
||||
return {
|
||||
...state,
|
||||
apps: [...state.apps, action.payload],
|
||||
};
|
||||
|
||||
case ActionType.deleteApp:
|
||||
return {
|
||||
...state,
|
||||
apps: [...state.apps].filter((app) => app.id !== action.payload),
|
||||
};
|
||||
|
||||
case ActionType.updateApp:
|
||||
const updatedAppIdx = state.apps.findIndex(
|
||||
(app) => app.id === action.payload.id
|
||||
);
|
||||
|
||||
return {
|
||||
...state,
|
||||
apps: [
|
||||
...state.apps.slice(0, updatedAppIdx),
|
||||
action.payload,
|
||||
...state.apps.slice(updatedAppIdx + 1),
|
||||
],
|
||||
};
|
||||
|
||||
case ActionType.reorderApps:
|
||||
return {
|
||||
...state,
|
||||
apps: action.payload,
|
||||
};
|
||||
|
||||
case ActionType.sortApps:
|
||||
return {
|
||||
...state,
|
||||
apps: sortData<App>(state.apps, action.payload),
|
||||
};
|
||||
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
};
|
166
client/src/store/reducers/bookmark.ts
Normal file
166
client/src/store/reducers/bookmark.ts
Normal file
|
@ -0,0 +1,166 @@
|
|||
import { Category } from '../../interfaces';
|
||||
import { sortData } from '../../utility';
|
||||
import { ActionType } from '../action-types';
|
||||
import { Action } from '../actions';
|
||||
|
||||
interface BookmarksState {
|
||||
loading: boolean;
|
||||
errors: string | undefined;
|
||||
categories: Category[];
|
||||
}
|
||||
|
||||
const initialState: BookmarksState = {
|
||||
loading: true,
|
||||
errors: undefined,
|
||||
categories: [],
|
||||
};
|
||||
|
||||
export const bookmarksReducer = (
|
||||
state: BookmarksState = initialState,
|
||||
action: Action
|
||||
): BookmarksState => {
|
||||
switch (action.type) {
|
||||
case ActionType.getCategories:
|
||||
return {
|
||||
...state,
|
||||
loading: true,
|
||||
errors: undefined,
|
||||
};
|
||||
|
||||
case ActionType.getCategoriesSuccess:
|
||||
return {
|
||||
...state,
|
||||
loading: false,
|
||||
categories: action.payload,
|
||||
};
|
||||
|
||||
case ActionType.addCategory:
|
||||
return {
|
||||
...state,
|
||||
categories: [...state.categories, { ...action.payload, bookmarks: [] }],
|
||||
};
|
||||
|
||||
case ActionType.addBookmark:
|
||||
const categoryIdx = state.categories.findIndex(
|
||||
(category) => category.id === action.payload.categoryId
|
||||
);
|
||||
|
||||
return {
|
||||
...state,
|
||||
categories: [
|
||||
...state.categories.slice(0, categoryIdx),
|
||||
{
|
||||
...state.categories[categoryIdx],
|
||||
bookmarks: [
|
||||
...state.categories[categoryIdx].bookmarks,
|
||||
action.payload,
|
||||
],
|
||||
},
|
||||
...state.categories.slice(categoryIdx + 1),
|
||||
],
|
||||
};
|
||||
|
||||
case ActionType.pinCategory:
|
||||
const pinnedCategoryIdx = state.categories.findIndex(
|
||||
(category) => category.id === action.payload.id
|
||||
);
|
||||
|
||||
return {
|
||||
...state,
|
||||
categories: [
|
||||
...state.categories.slice(0, pinnedCategoryIdx),
|
||||
action.payload,
|
||||
...state.categories.slice(pinnedCategoryIdx + 1),
|
||||
],
|
||||
};
|
||||
|
||||
case ActionType.deleteCategory:
|
||||
const deletedCategoryIdx = state.categories.findIndex(
|
||||
(category) => category.id === action.payload
|
||||
);
|
||||
|
||||
return {
|
||||
...state,
|
||||
categories: [
|
||||
...state.categories.slice(0, deletedCategoryIdx),
|
||||
...state.categories.slice(deletedCategoryIdx + 1),
|
||||
],
|
||||
};
|
||||
|
||||
case ActionType.updateCategory:
|
||||
const updatedCategoryIdx = state.categories.findIndex(
|
||||
(category) => category.id === action.payload.id
|
||||
);
|
||||
|
||||
return {
|
||||
...state,
|
||||
categories: [
|
||||
...state.categories.slice(0, updatedCategoryIdx),
|
||||
action.payload,
|
||||
...state.categories.slice(updatedCategoryIdx + 1),
|
||||
],
|
||||
};
|
||||
|
||||
case ActionType.deleteBookmark:
|
||||
const categoryInUpdateIdx = state.categories.findIndex(
|
||||
(category) => category.id === action.payload.categoryId
|
||||
);
|
||||
|
||||
return {
|
||||
...state,
|
||||
categories: [
|
||||
...state.categories.slice(0, categoryInUpdateIdx),
|
||||
{
|
||||
...state.categories[categoryInUpdateIdx],
|
||||
bookmarks: state.categories[categoryInUpdateIdx].bookmarks.filter(
|
||||
(bookmark) => bookmark.id !== action.payload.bookmarkId
|
||||
),
|
||||
},
|
||||
...state.categories.slice(categoryInUpdateIdx + 1),
|
||||
],
|
||||
};
|
||||
|
||||
case ActionType.updateBookmark:
|
||||
const parentCategoryIdx = state.categories.findIndex(
|
||||
(category) => category.id === action.payload.categoryId
|
||||
);
|
||||
const updatedBookmarkIdx = state.categories[
|
||||
parentCategoryIdx
|
||||
].bookmarks.findIndex((bookmark) => bookmark.id === action.payload.id);
|
||||
|
||||
return {
|
||||
...state,
|
||||
categories: [
|
||||
...state.categories.slice(0, parentCategoryIdx),
|
||||
{
|
||||
...state.categories[parentCategoryIdx],
|
||||
bookmarks: [
|
||||
...state.categories[parentCategoryIdx].bookmarks.slice(
|
||||
0,
|
||||
updatedBookmarkIdx
|
||||
),
|
||||
action.payload,
|
||||
...state.categories[parentCategoryIdx].bookmarks.slice(
|
||||
updatedBookmarkIdx + 1
|
||||
),
|
||||
],
|
||||
},
|
||||
...state.categories.slice(parentCategoryIdx + 1),
|
||||
],
|
||||
};
|
||||
|
||||
case ActionType.sortCategories:
|
||||
return {
|
||||
...state,
|
||||
categories: sortData<Category>(state.categories, action.payload),
|
||||
};
|
||||
|
||||
case ActionType.reorderCategories:
|
||||
return {
|
||||
...state,
|
||||
categories: action.payload,
|
||||
};
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
};
|
|
@ -3,11 +3,15 @@ import { combineReducers } from 'redux';
|
|||
import { themeReducer } from './theme';
|
||||
import { configReducer } from './config';
|
||||
import { notificationReducer } from './notification';
|
||||
import { appsReducer } from './app';
|
||||
import { bookmarksReducer } from './bookmark';
|
||||
|
||||
export const reducers = combineReducers({
|
||||
theme: themeReducer,
|
||||
config: configReducer,
|
||||
notification: notificationReducer,
|
||||
apps: appsReducer,
|
||||
bookmarks: bookmarksReducer,
|
||||
});
|
||||
|
||||
export type State = ReturnType<typeof reducers>;
|
||||
|
|
Loading…
Reference in a new issue