diff --git a/client/.env b/client/.env
index 3df1a71..69d1f92 100644
--- a/client/.env
+++ b/client/.env
@@ -1 +1 @@
-REACT_APP_VERSION=1.3.3
\ No newline at end of file
+REACT_APP_VERSION=1.3.5
\ No newline at end of file
diff --git a/client/src/components/Settings/OtherSettings/OtherSettings.tsx b/client/src/components/Settings/OtherSettings/OtherSettings.tsx
index 5df8be7..50b04b5 100644
--- a/client/src/components/Settings/OtherSettings/OtherSettings.tsx
+++ b/client/src/components/Settings/OtherSettings/OtherSettings.tsx
@@ -2,7 +2,7 @@ import { useState, useEffect, ChangeEvent, FormEvent } from 'react';
// Redux
import { connect } from 'react-redux';
-import { createNotification, updateConfig } from '../../../store/actions';
+import { createNotification, updateConfig, sortApps } from '../../../store/actions';
// Typescript
import { GlobalState, NewNotification, SettingsForm } from '../../../interfaces';
@@ -17,6 +17,7 @@ import { searchConfig } from '../../../utility';
interface ComponentProps {
createNotification: (notification: NewNotification) => void;
updateConfig: (formData: SettingsForm) => void;
+ sortApps: () => void;
loading: boolean;
}
@@ -26,7 +27,8 @@ const OtherSettings = (props: ComponentProps): JSX.Element => {
customTitle: document.title,
pinAppsByDefault: 1,
pinCategoriesByDefault: 1,
- hideHeader: 0
+ hideHeader: 0,
+ useOrdering: 'createdAt'
})
// Get config
@@ -35,7 +37,8 @@ const OtherSettings = (props: ComponentProps): JSX.Element => {
customTitle: searchConfig('customTitle', 'Flame'),
pinAppsByDefault: searchConfig('pinAppsByDefault', 1),
pinCategoriesByDefault: searchConfig('pinCategoriesByDefault', 1),
- hideHeader: searchConfig('hideHeader', 0)
+ hideHeader: searchConfig('hideHeader', 0),
+ useOrdering: searchConfig('useOrdering', 'createdAt')
})
}, [props.loading]);
@@ -46,8 +49,11 @@ const OtherSettings = (props: ComponentProps): JSX.Element => {
// Save settings
await props.updateConfig(formData);
- // update local page title
+ // Update local page title
document.title = formData.customTitle;
+
+ // Get sorted apps
+ props.sortApps();
}
// Input handler
@@ -113,6 +119,19 @@ const OtherSettings = (props: ComponentProps): JSX.Element => {
+
+
+
+
)
@@ -124,4 +143,4 @@ const mapStateToProps = (state: GlobalState) => {
}
}
-export default connect(mapStateToProps, { createNotification, updateConfig })(OtherSettings);
\ No newline at end of file
+export default connect(mapStateToProps, { createNotification, updateConfig, sortApps })(OtherSettings);
\ No newline at end of file
diff --git a/client/src/interfaces/Category.ts b/client/src/interfaces/Category.ts
index 926987d..0f9f8f9 100644
--- a/client/src/interfaces/Category.ts
+++ b/client/src/interfaces/Category.ts
@@ -3,6 +3,7 @@ import { Model, Bookmark } from '.';
export interface Category extends Model {
name: string;
isPinned: boolean;
+ orderId: number;
bookmarks: Bookmark[];
}
diff --git a/client/src/store/actions/actionTypes.ts b/client/src/store/actions/actionTypes.ts
index 42d863e..769bafa 100644
--- a/client/src/store/actions/actionTypes.ts
+++ b/client/src/store/actions/actionTypes.ts
@@ -7,7 +7,8 @@ import {
AddAppAction,
DeleteAppAction,
UpdateAppAction,
- ReorderAppAction,
+ ReorderAppsAction,
+ SortAppsAction,
// Categories
GetCategoriesAction,
AddCategoryAction,
@@ -38,7 +39,8 @@ export enum ActionTypes {
addAppSuccess = 'ADD_APP_SUCCESS',
deleteApp = 'DELETE_APP',
updateApp = 'UPDATE_APP',
- reorderApp = 'REORDER_APP',
+ reorderApps = 'REORDER_APPS',
+ sortApps = 'SORT_APPS',
// Categories
getCategories = 'GET_CATEGORIES',
getCategoriesSuccess = 'GET_CATEGORIES_SUCCESS',
@@ -68,7 +70,8 @@ export type Action =
AddAppAction |
DeleteAppAction |
UpdateAppAction |
- ReorderAppAction |
+ ReorderAppsAction |
+ SortAppsAction |
// Categories
GetCategoriesAction |
AddCategoryAction |
diff --git a/client/src/store/actions/app.ts b/client/src/store/actions/app.ts
index 5c7db15..8dc9e94 100644
--- a/client/src/store/actions/app.ts
+++ b/client/src/store/actions/app.ts
@@ -1,7 +1,7 @@
import axios from 'axios';
import { Dispatch } from 'redux';
import { ActionTypes } from './actionTypes';
-import { App, ApiResponse, NewApp } from '../../interfaces';
+import { App, ApiResponse, NewApp, Config } from '../../interfaces';
import { CreateNotificationAction } from './notification';
export interface GetAppsAction {
@@ -73,10 +73,13 @@ export const addApp = (formData: NewApp) => async (dispatch: Dispatch) => {
}
})
- dispatch({
+ await dispatch({
type: ActionTypes.addAppSuccess,
payload: res.data.data
})
+
+ // Sort apps
+ dispatch(sortApps())
} catch (err) {
console.log(err);
}
@@ -125,17 +128,20 @@ export const updateApp = (id: number, formData: NewApp) => async (dispatch: Disp
}
})
- dispatch({
+ await dispatch({
type: ActionTypes.updateApp,
payload: res.data.data
})
+
+ // Sort apps
+ dispatch(sortApps())
} catch (err) {
console.log(err);
}
}
-export interface ReorderAppAction {
- type: ActionTypes.reorderApp;
+export interface ReorderAppsAction {
+ type: ActionTypes.reorderApps;
payload: App[]
}
@@ -146,7 +152,7 @@ interface ReorderQuery {
}[]
}
-export const reorderApp = (apps: App[]) => async (dispatch: Dispatch) => {
+export const reorderApps = (apps: App[]) => async (dispatch: Dispatch) => {
try {
const updateQuery: ReorderQuery = { apps: [] }
@@ -157,11 +163,39 @@ export const reorderApp = (apps: App[]) => async (dispatch: Dispatch) => {
await axios.put<{}>('/api/apps/0/reorder', updateQuery);
- dispatch({
- type: ActionTypes.reorderApp,
+ dispatch({
+ type: ActionTypes.createNotification,
+ payload: {
+ title: 'Success',
+ message: 'New order saved'
+ }
+ })
+
+ dispatch({
+ type: ActionTypes.reorderApps,
payload: apps
})
} catch (err) {
console.log(err);
}
+}
+
+export interface SortAppsAction {
+ type: ActionTypes.sortApps;
+ payload: {};
+}
+
+export const sortApps = () => async (dispatch: Dispatch) => {
+ try {
+ const res = await axios.get>('/api/config/useOrdering');
+
+ console.log(res.data.data);
+
+ dispatch({
+ type: ActionTypes.sortApps,
+ payload: res.data.data.value
+ })
+ } catch (err) {
+ console.log(err);
+ }
}
\ No newline at end of file
diff --git a/client/src/store/reducers/app.ts b/client/src/store/reducers/app.ts
index dcdb6ef..0935819 100644
--- a/client/src/store/reducers/app.ts
+++ b/client/src/store/reducers/app.ts
@@ -1,5 +1,6 @@
import { ActionTypes, Action } from '../actions';
import { App } from '../../interfaces/App';
+import { sortData } from '../../utility';
export interface State {
loading: boolean;
@@ -52,15 +53,9 @@ const pinApp = (state: State, action: Action): State => {
}
const addAppSuccess = (state: State, action: Action): State => {
- const tmpApps: App[] = [...state.apps, action.payload].sort((a: App, b: App) => {
- if (a.name.toLowerCase() < b.name.toLowerCase()) { return -1 }
- if (a.name.toLowerCase() > b.name.toLowerCase()) { return 1 }
- return 0;
- });
-
return {
...state,
- apps: tmpApps
+ apps: [...state.apps, action.payload]
}
}
@@ -89,13 +84,22 @@ const updateApp = (state: State, action: Action): State => {
}
}
-const reorderApp = (state: State, action: Action): State => {
+const reorderApps = (state: State, action: Action): State => {
return {
...state,
apps: action.payload
}
}
+const sortApps = (state: State, action: Action): State => {
+ const sortedApps = sortData(state.apps, action.payload);
+
+ return {
+ ...state,
+ apps: sortedApps
+ }
+}
+
const appReducer = (state = initialState, action: Action) => {
switch (action.type) {
case ActionTypes.getApps: return getApps(state, action);
@@ -105,7 +109,8 @@ const appReducer = (state = initialState, action: Action) => {
case ActionTypes.addAppSuccess: return addAppSuccess(state, action);
case ActionTypes.deleteApp: return deleteApp(state, action);
case ActionTypes.updateApp: return updateApp(state, action);
- case ActionTypes.reorderApp: return reorderApp(state, action);
+ case ActionTypes.reorderApps: return reorderApps(state, action);
+ case ActionTypes.sortApps: return sortApps(state, action);
default: return state;
}
}
diff --git a/client/src/utility/index.ts b/client/src/utility/index.ts
index 1422624..a5407b2 100644
--- a/client/src/utility/index.ts
+++ b/client/src/utility/index.ts
@@ -1,4 +1,5 @@
export * from './iconParser';
export * from './urlParser';
export * from './searchConfig';
-export * from './checkVersion';
\ No newline at end of file
+export * from './checkVersion';
+export * from './sortData';
\ No newline at end of file
diff --git a/client/src/utility/sortData.ts b/client/src/utility/sortData.ts
new file mode 100644
index 0000000..c1e9803
--- /dev/null
+++ b/client/src/utility/sortData.ts
@@ -0,0 +1,29 @@
+interface Data {
+ name: string;
+ orderId: number;
+ createdAt: Date;
+}
+
+export const sortData = (array: T[], field: string): T[] => {
+ const sortedData = array.slice();
+
+ if (field === 'name') {
+ sortedData.sort((a: T, b: T) => {
+ return a.name.localeCompare(b.name, 'en', { sensitivity: 'base' })
+ })
+ } else if (field === 'orderId') {
+ sortedData.sort((a: T, b: T) => {
+ if (a.orderId < b.orderId) { return -1 }
+ if (a.orderId > b.orderId) { return 1 }
+ return 0;
+ })
+ } else {
+ sortedData.sort((a: T, b: T) => {
+ if (a.createdAt < b.createdAt) { return -1 }
+ if (a.createdAt > b.createdAt) { return 1 }
+ return 0;
+ })
+ }
+
+ return sortedData;
+}
\ No newline at end of file
diff --git a/models/Category.js b/models/Category.js
index 5f82633..9c9eda6 100644
--- a/models/Category.js
+++ b/models/Category.js
@@ -9,6 +9,11 @@ const Category = sequelize.define('Category', {
isPinned: {
type: DataTypes.BOOLEAN,
defaultValue: false
+ },
+ orderId: {
+ type: DataTypes.INTEGER,
+ allowNull: true,
+ defaultValue: null
}
}, {
tableName: 'categories'