mirror of https://github.com/usememos/memos
refactor: use `redux`
parent
2e9152e223
commit
c2e5a1a524
@ -0,0 +1,18 @@
|
||||
import store from "../store";
|
||||
import { setEditMemoId, setMarkMemoId } from "../store/modules/editor";
|
||||
|
||||
const editorStateService = {
|
||||
getState: () => {
|
||||
return store.getState().editor;
|
||||
},
|
||||
|
||||
setEditMemo: (editMemoId: MemoId) => {
|
||||
store.dispatch(setEditMemoId(editMemoId));
|
||||
},
|
||||
|
||||
setMarkMemo: (markMemoId: MemoId) => {
|
||||
store.dispatch(setMarkMemoId(markMemoId));
|
||||
},
|
||||
};
|
||||
|
||||
export default editorStateService;
|
@ -1,50 +0,0 @@
|
||||
import { storage } from "../helpers/storage";
|
||||
import appStore from "../stores/appStore";
|
||||
import { AppSetting } from "../stores/globalStateStore";
|
||||
|
||||
class GlobalStateService {
|
||||
constructor() {
|
||||
const cachedSetting = storage.get(["shouldSplitMemoWord", "shouldHideImageUrl", "shouldUseMarkdownParser"]);
|
||||
const defaultAppSetting = {
|
||||
shouldSplitMemoWord: cachedSetting.shouldSplitMemoWord ?? true,
|
||||
shouldHideImageUrl: cachedSetting.shouldHideImageUrl ?? true,
|
||||
shouldUseMarkdownParser: cachedSetting.shouldUseMarkdownParser ?? true,
|
||||
};
|
||||
|
||||
this.setAppSetting(defaultAppSetting);
|
||||
}
|
||||
|
||||
public getState = () => {
|
||||
return appStore.getState().globalState;
|
||||
};
|
||||
|
||||
public setEditMemoId = (editMemoId: MemoId) => {
|
||||
appStore.dispatch({
|
||||
type: "SET_EDIT_MEMO_ID",
|
||||
payload: {
|
||||
editMemoId,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
public setMarkMemoId = (markMemoId: MemoId) => {
|
||||
appStore.dispatch({
|
||||
type: "SET_MARK_MEMO_ID",
|
||||
payload: {
|
||||
markMemoId,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
public setAppSetting = (appSetting: Partial<AppSetting>) => {
|
||||
appStore.dispatch({
|
||||
type: "SET_APP_SETTING",
|
||||
payload: appSetting,
|
||||
});
|
||||
storage.set(appSetting);
|
||||
};
|
||||
}
|
||||
|
||||
const globalStateService = new GlobalStateService();
|
||||
|
||||
export default globalStateService;
|
@ -1,8 +1,8 @@
|
||||
import globalStateService from "./globalStateService";
|
||||
import editorStateService from "./editorStateService";
|
||||
import locationService from "./locationService";
|
||||
import memoService from "./memoService";
|
||||
import shortcutService from "./shortcutService";
|
||||
import userService from "./userService";
|
||||
import resourceService from "./resourceService";
|
||||
|
||||
export { globalStateService, locationService, memoService, shortcutService, userService, resourceService };
|
||||
export { editorStateService, locationService, memoService, shortcutService, userService, resourceService };
|
||||
|
@ -1,97 +1,77 @@
|
||||
import userService from "./userService";
|
||||
import api from "../helpers/api";
|
||||
import appStore from "../stores/appStore";
|
||||
import { UNKNOWN_ID } from "../helpers/consts";
|
||||
|
||||
class ShortcutService {
|
||||
public getState() {
|
||||
return appStore.getState().shortcutState;
|
||||
}
|
||||
|
||||
public async getMyAllShortcuts() {
|
||||
import store from "../store/";
|
||||
import { deleteShortcut, patchShortcut, setShortcuts } from "../store/modules/shortcut";
|
||||
|
||||
const convertResponseModelShortcut = (shortcut: Shortcut): Shortcut => {
|
||||
return {
|
||||
...shortcut,
|
||||
createdTs: shortcut.createdTs * 1000,
|
||||
updatedTs: shortcut.updatedTs * 1000,
|
||||
};
|
||||
};
|
||||
|
||||
const shortcutService = {
|
||||
getState: () => {
|
||||
return store.getState().shortcut;
|
||||
},
|
||||
|
||||
getMyAllShortcuts: async () => {
|
||||
if (!userService.getState().user) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const data = await api.getMyShortcuts();
|
||||
appStore.dispatch({
|
||||
type: "SET_SHORTCUTS",
|
||||
payload: {
|
||||
shortcuts: data.map((s) => this.convertResponseModelShortcut(s)),
|
||||
},
|
||||
});
|
||||
return data;
|
||||
}
|
||||
const shortcuts = data.map((s) => convertResponseModelShortcut(s));
|
||||
store.dispatch(setShortcuts(shortcuts));
|
||||
return shortcuts;
|
||||
},
|
||||
|
||||
public getShortcutById(id: ShortcutId) {
|
||||
getShortcutById: (id: ShortcutId) => {
|
||||
if (id === UNKNOWN_ID) {
|
||||
return null;
|
||||
}
|
||||
|
||||
for (const s of this.getState().shortcuts) {
|
||||
for (const s of shortcutService.getState().shortcuts) {
|
||||
if (s.id === id) {
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
},
|
||||
|
||||
public pushShortcut(shortcut: Shortcut) {
|
||||
appStore.dispatch({
|
||||
type: "INSERT_SHORTCUT",
|
||||
payload: {
|
||||
shortcut: {
|
||||
...shortcut,
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
pushShortcut: (shortcut: Shortcut) => {
|
||||
store.dispatch(setShortcuts(shortcutService.getState().shortcuts.concat(shortcut)));
|
||||
},
|
||||
|
||||
public editShortcut(shortcut: Shortcut) {
|
||||
appStore.dispatch({
|
||||
type: "UPDATE_SHORTCUT",
|
||||
payload: shortcut,
|
||||
});
|
||||
}
|
||||
editShortcut: (shortcut: Shortcut) => {
|
||||
store.dispatch(patchShortcut(shortcut));
|
||||
},
|
||||
|
||||
public async deleteShortcut(shortcutId: ShortcutId) {
|
||||
deleteShortcutById: async (shortcutId: ShortcutId) => {
|
||||
await api.deleteShortcutById(shortcutId);
|
||||
appStore.dispatch({
|
||||
type: "DELETE_SHORTCUT_BY_ID",
|
||||
payload: {
|
||||
id: shortcutId,
|
||||
},
|
||||
});
|
||||
}
|
||||
store.dispatch(deleteShortcut(shortcutId));
|
||||
},
|
||||
|
||||
public async createShortcut(title: string, payload: string) {
|
||||
createShortcut: async (title: string, payload: string) => {
|
||||
const data = await api.createShortcut(title, payload);
|
||||
return data;
|
||||
}
|
||||
shortcutService.pushShortcut(convertResponseModelShortcut(data));
|
||||
},
|
||||
|
||||
public async updateShortcut(shortcutId: ShortcutId, title: string, payload: string) {
|
||||
updateShortcut: async (shortcutId: ShortcutId, title: string, payload: string) => {
|
||||
const data = await api.updateShortcut(shortcutId, title, payload);
|
||||
return data;
|
||||
}
|
||||
store.dispatch(patchShortcut(convertResponseModelShortcut(data)));
|
||||
},
|
||||
|
||||
public async pinShortcut(shortcutId: ShortcutId) {
|
||||
pinShortcut: async (shortcutId: ShortcutId) => {
|
||||
await api.pinShortcut(shortcutId);
|
||||
}
|
||||
},
|
||||
|
||||
public async unpinShortcut(shortcutId: ShortcutId) {
|
||||
unpinShortcut: async (shortcutId: ShortcutId) => {
|
||||
await api.unpinShortcut(shortcutId);
|
||||
}
|
||||
|
||||
public convertResponseModelShortcut(shortcut: Shortcut): Shortcut {
|
||||
return {
|
||||
...shortcut,
|
||||
createdTs: shortcut.createdTs * 1000,
|
||||
updatedTs: shortcut.updatedTs * 1000,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
const shortcutService = new ShortcutService();
|
||||
},
|
||||
};
|
||||
|
||||
export default shortcutService;
|
||||
|
@ -1,68 +1,55 @@
|
||||
import api from "../helpers/api";
|
||||
import appStore from "../stores/appStore";
|
||||
|
||||
class UserService {
|
||||
public getState() {
|
||||
return appStore.getState().userState;
|
||||
}
|
||||
|
||||
public async doSignIn() {
|
||||
import { signin, signout } from "../store/modules/user";
|
||||
import store from "../store";
|
||||
|
||||
const convertResponseModelUser = (user: User): User => {
|
||||
return {
|
||||
...user,
|
||||
createdTs: user.createdTs * 1000,
|
||||
updatedTs: user.updatedTs * 1000,
|
||||
};
|
||||
};
|
||||
|
||||
const userService = {
|
||||
getState: () => {
|
||||
return store.getState().user;
|
||||
},
|
||||
|
||||
doSignIn: async () => {
|
||||
const user = await api.getUser();
|
||||
if (user) {
|
||||
appStore.dispatch({
|
||||
type: "LOGIN",
|
||||
payload: {
|
||||
user: this.convertResponseModelUser(user),
|
||||
},
|
||||
});
|
||||
store.dispatch(signin(convertResponseModelUser(user)));
|
||||
} else {
|
||||
userService.doSignOut();
|
||||
}
|
||||
return user;
|
||||
}
|
||||
},
|
||||
|
||||
public async doSignOut() {
|
||||
appStore.dispatch({
|
||||
type: "SIGN_OUT",
|
||||
payload: null,
|
||||
});
|
||||
doSignOut: async () => {
|
||||
store.dispatch(signout);
|
||||
api.signout().catch(() => {
|
||||
// do nth
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
public async updateUsername(name: string): Promise<void> {
|
||||
updateUsername: async (name: string): Promise<void> => {
|
||||
await api.patchUser({
|
||||
name,
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
public async updatePassword(password: string): Promise<void> {
|
||||
updatePassword: async (password: string): Promise<void> => {
|
||||
await api.patchUser({
|
||||
password,
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
public async resetOpenId(): Promise<string> {
|
||||
resetOpenId: async (): Promise<string> => {
|
||||
const user = await api.patchUser({
|
||||
resetOpenId: true,
|
||||
});
|
||||
appStore.dispatch({
|
||||
type: "RESET_OPENID",
|
||||
payload: user.openId,
|
||||
});
|
||||
return user.openId;
|
||||
}
|
||||
|
||||
private convertResponseModelUser(user: User): User {
|
||||
return {
|
||||
...user,
|
||||
createdTs: user.createdTs * 1000,
|
||||
updatedTs: user.updatedTs * 1000,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
const userService = new UserService();
|
||||
},
|
||||
};
|
||||
|
||||
export default userService;
|
||||
|
@ -0,0 +1,25 @@
|
||||
import { configureStore } from "@reduxjs/toolkit";
|
||||
import { TypedUseSelectorHook, useDispatch, useSelector } from "react-redux";
|
||||
import userReducer from "./modules/user";
|
||||
import memoReducer from "./modules/memo";
|
||||
import editorReducer from "./modules/editor";
|
||||
import shortcutReducer from "./modules/shortcut";
|
||||
import locationReducer from "./modules/location";
|
||||
|
||||
const store = configureStore({
|
||||
reducer: {
|
||||
user: userReducer,
|
||||
memo: memoReducer,
|
||||
editor: editorReducer,
|
||||
shortcut: shortcutReducer,
|
||||
location: locationReducer,
|
||||
},
|
||||
});
|
||||
|
||||
type AppState = ReturnType<typeof store.getState>;
|
||||
type AppDispatch = typeof store.dispatch;
|
||||
|
||||
export const useAppSelector: TypedUseSelectorHook<AppState> = useSelector;
|
||||
export const useAppDispatch = () => useDispatch<AppDispatch>();
|
||||
|
||||
export default store;
|
@ -0,0 +1,23 @@
|
||||
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
|
||||
|
||||
interface State {
|
||||
markMemoId?: MemoId;
|
||||
editMemoId?: MemoId;
|
||||
}
|
||||
|
||||
const editorSlice = createSlice({
|
||||
name: "editor",
|
||||
initialState: {} as State,
|
||||
reducers: {
|
||||
setMarkMemoId: (state, action: PayloadAction<Option<MemoId>>) => {
|
||||
state.markMemoId = action.payload;
|
||||
},
|
||||
setEditMemoId: (state, action: PayloadAction<Option<MemoId>>) => {
|
||||
state.editMemoId = action.payload;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export const { setEditMemoId, setMarkMemoId } = editorSlice.actions;
|
||||
|
||||
export default editorSlice.reducer;
|
@ -0,0 +1,62 @@
|
||||
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
|
||||
|
||||
interface State {
|
||||
pathname: AppRouter;
|
||||
hash: string;
|
||||
query?: Query;
|
||||
}
|
||||
|
||||
const getValidPathname = (pathname: string): AppRouter => {
|
||||
if (["/", "/signin"].includes(pathname)) {
|
||||
return pathname as AppRouter;
|
||||
} else {
|
||||
return "/";
|
||||
}
|
||||
};
|
||||
|
||||
const getStateFromLocation = () => {
|
||||
const { pathname, search, hash } = window.location;
|
||||
const urlParams = new URLSearchParams(search);
|
||||
const state: State = {
|
||||
pathname: getValidPathname(pathname),
|
||||
hash: hash,
|
||||
};
|
||||
|
||||
if (search !== "") {
|
||||
state.query = {};
|
||||
state.query.tag = urlParams.get("tag") ?? undefined;
|
||||
state.query.type = (urlParams.get("type") as MemoSpecType) ?? undefined;
|
||||
state.query.text = urlParams.get("text") ?? undefined;
|
||||
state.query.shortcutId = Number(urlParams.get("shortcutId")) ?? undefined;
|
||||
const from = parseInt(urlParams.get("from") ?? "0");
|
||||
const to = parseInt(urlParams.get("to") ?? "0");
|
||||
if (to > from && to !== 0) {
|
||||
state.query.duration = {
|
||||
from,
|
||||
to,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return state;
|
||||
};
|
||||
|
||||
const locationSlice = createSlice({
|
||||
name: "location",
|
||||
initialState: getStateFromLocation(),
|
||||
reducers: {
|
||||
updateStateWithLocation: () => {
|
||||
return getStateFromLocation();
|
||||
},
|
||||
setPathname: (state, action: PayloadAction<AppRouter>) => {
|
||||
state.pathname = action.payload;
|
||||
},
|
||||
setQuery: (state, action: PayloadAction<Partial<Query>>) => {
|
||||
state.query = action.payload;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export const { setPathname, setQuery, updateStateWithLocation } = locationSlice.actions;
|
||||
|
||||
export default locationSlice.reducer;
|
@ -0,0 +1,44 @@
|
||||
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
|
||||
|
||||
interface State {
|
||||
memos: Memo[];
|
||||
tags: string[];
|
||||
}
|
||||
|
||||
const memoSlice = createSlice({
|
||||
name: "memo",
|
||||
initialState: {
|
||||
memos: [],
|
||||
tags: [],
|
||||
} as State,
|
||||
reducers: {
|
||||
setMemos: (state, action: PayloadAction<Memo[]>) => {
|
||||
state.memos = action.payload;
|
||||
},
|
||||
setTags: (state, action: PayloadAction<string[]>) => {
|
||||
state.tags = action.payload;
|
||||
},
|
||||
createMemo: (state, action: PayloadAction<Memo>) => {
|
||||
state.memos = state.memos.concat(action.payload);
|
||||
},
|
||||
patchMemo: (state, action: PayloadAction<Partial<Memo>>) => {
|
||||
state.memos = state.memos.map((m) => {
|
||||
if (m.id === action.payload.id) {
|
||||
return {
|
||||
...m,
|
||||
...action.payload,
|
||||
};
|
||||
} else {
|
||||
return m;
|
||||
}
|
||||
});
|
||||
},
|
||||
deleteMemo: (state, action: PayloadAction<MemoId>) => {
|
||||
state.memos = [...state.memos].filter((memo) => memo.id !== action.payload);
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export const { setMemos, setTags, createMemo, patchMemo, deleteMemo } = memoSlice.actions;
|
||||
|
||||
export default memoSlice.reducer;
|
@ -0,0 +1,39 @@
|
||||
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
|
||||
|
||||
interface State {
|
||||
shortcuts: Shortcut[];
|
||||
}
|
||||
|
||||
const shortcutSlice = createSlice({
|
||||
name: "memo",
|
||||
initialState: {
|
||||
shortcuts: [],
|
||||
} as State,
|
||||
reducers: {
|
||||
setShortcuts: (state, action: PayloadAction<Shortcut[]>) => {
|
||||
state.shortcuts = action.payload;
|
||||
},
|
||||
createShortcut: (state, action: PayloadAction<Shortcut>) => {
|
||||
state.shortcuts = state.shortcuts.concat(action.payload);
|
||||
},
|
||||
patchShortcut: (state, action: PayloadAction<Partial<Shortcut>>) => {
|
||||
state.shortcuts = state.shortcuts.map((s) => {
|
||||
if (s.id === action.payload.id) {
|
||||
return {
|
||||
...s,
|
||||
...action.payload,
|
||||
};
|
||||
} else {
|
||||
return s;
|
||||
}
|
||||
});
|
||||
},
|
||||
deleteShortcut: (state, action: PayloadAction<ShortcutId>) => {
|
||||
state.shortcuts = [...state.shortcuts].filter((shortcut) => shortcut.id !== action.payload);
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export const { setShortcuts, createShortcut, patchShortcut, deleteShortcut } = shortcutSlice.actions;
|
||||
|
||||
export default shortcutSlice.reducer;
|
@ -0,0 +1,34 @@
|
||||
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
|
||||
|
||||
interface State {
|
||||
user?: User;
|
||||
}
|
||||
|
||||
const userSlice = createSlice({
|
||||
name: "user",
|
||||
initialState: {} as State,
|
||||
reducers: {
|
||||
signin: (state, action: PayloadAction<User>) => {
|
||||
return {
|
||||
...state,
|
||||
user: action.payload,
|
||||
};
|
||||
},
|
||||
signout: (state) => {
|
||||
return {
|
||||
...state,
|
||||
user: undefined,
|
||||
};
|
||||
},
|
||||
patchUser: (state, action: PayloadAction<Partial<User>>) => {
|
||||
state.user = {
|
||||
...state.user,
|
||||
...action.payload,
|
||||
} as User;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export const { signin, signout, patchUser } = userSlice.actions;
|
||||
|
||||
export default userSlice.reducer;
|
@ -1,6 +0,0 @@
|
||||
import { createContext } from "react";
|
||||
import appStore from "./appStore";
|
||||
|
||||
const appContext = createContext(appStore.getState());
|
||||
|
||||
export default appContext;
|
@ -1,36 +0,0 @@
|
||||
import combineReducers from "../labs/combineReducers";
|
||||
import createStore from "../labs/createStore";
|
||||
import * as globalStore from "./globalStateStore";
|
||||
import * as locationStore from "./locationStore";
|
||||
import * as memoStore from "./memoStore";
|
||||
import * as userStore from "./userStore";
|
||||
import * as shortcutStore from "./shortcutStore";
|
||||
|
||||
interface AppState {
|
||||
globalState: globalStore.State;
|
||||
locationState: locationStore.State;
|
||||
memoState: memoStore.State;
|
||||
userState: userStore.State;
|
||||
shortcutState: shortcutStore.State;
|
||||
}
|
||||
|
||||
type AppStateActions = globalStore.Actions | locationStore.Actions | memoStore.Actions | userStore.Actions | shortcutStore.Actions;
|
||||
|
||||
const appStore = createStore<AppState, AppStateActions>(
|
||||
{
|
||||
globalState: globalStore.defaultState,
|
||||
locationState: locationStore.defaultState,
|
||||
memoState: memoStore.defaultState,
|
||||
userState: userStore.defaultState,
|
||||
shortcutState: shortcutStore.defaultState,
|
||||
},
|
||||
combineReducers<AppState, AppStateActions>({
|
||||
globalState: globalStore.reducer,
|
||||
locationState: locationStore.reducer,
|
||||
memoState: memoStore.reducer,
|
||||
userState: userStore.reducer,
|
||||
shortcutState: shortcutStore.reducer,
|
||||
})
|
||||
);
|
||||
|
||||
export default appStore;
|
@ -1,75 +0,0 @@
|
||||
import { UNKNOWN_ID } from "../helpers/consts";
|
||||
|
||||
export interface AppSetting {
|
||||
shouldSplitMemoWord: boolean;
|
||||
shouldHideImageUrl: boolean;
|
||||
shouldUseMarkdownParser: boolean;
|
||||
}
|
||||
|
||||
export interface State extends AppSetting {
|
||||
markMemoId: MemoId;
|
||||
editMemoId: MemoId;
|
||||
}
|
||||
|
||||
interface SetMarkMemoIdAction {
|
||||
type: "SET_MARK_MEMO_ID";
|
||||
payload: {
|
||||
markMemoId: MemoId;
|
||||
};
|
||||
}
|
||||
|
||||
interface SetEditMemoIdAction {
|
||||
type: "SET_EDIT_MEMO_ID";
|
||||
payload: {
|
||||
editMemoId: MemoId;
|
||||
};
|
||||
}
|
||||
|
||||
interface SetAppSettingAction {
|
||||
type: "SET_APP_SETTING";
|
||||
payload: Partial<AppSetting>;
|
||||
}
|
||||
|
||||
export type Actions = SetEditMemoIdAction | SetMarkMemoIdAction | SetAppSettingAction;
|
||||
|
||||
export function reducer(state: State, action: Actions) {
|
||||
switch (action.type) {
|
||||
case "SET_MARK_MEMO_ID": {
|
||||
if (action.payload.markMemoId === state.markMemoId) {
|
||||
return state;
|
||||
}
|
||||
|
||||
return {
|
||||
...state,
|
||||
markMemoId: action.payload.markMemoId,
|
||||
};
|
||||
}
|
||||
case "SET_EDIT_MEMO_ID": {
|
||||
if (action.payload.editMemoId === state.editMemoId) {
|
||||
return state;
|
||||
}
|
||||
|
||||
return {
|
||||
...state,
|
||||
editMemoId: action.payload.editMemoId,
|
||||
};
|
||||
}
|
||||
case "SET_APP_SETTING": {
|
||||
return {
|
||||
...state,
|
||||
...action.payload,
|
||||
};
|
||||
}
|
||||
default: {
|
||||
return state;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const defaultState: State = {
|
||||
markMemoId: UNKNOWN_ID,
|
||||
editMemoId: UNKNOWN_ID,
|
||||
shouldSplitMemoWord: true,
|
||||
shouldHideImageUrl: true,
|
||||
shouldUseMarkdownParser: true,
|
||||
};
|
@ -1,187 +0,0 @@
|
||||
export type State = AppLocation;
|
||||
|
||||
interface SetLocationAction {
|
||||
type: "SET_LOCATION";
|
||||
payload: State;
|
||||
}
|
||||
|
||||
interface SetPathnameAction {
|
||||
type: "SET_PATHNAME";
|
||||
payload: {
|
||||
pathname: string;
|
||||
};
|
||||
}
|
||||
|
||||
interface SetQueryAction {
|
||||
type: "SET_QUERY";
|
||||
payload: Query;
|
||||
}
|
||||
|
||||
interface SetShortcutIdAction {
|
||||
type: "SET_SHORTCUT_ID";
|
||||
payload: ShortcutId | undefined;
|
||||
}
|
||||
|
||||
interface SetTagQueryAction {
|
||||
type: "SET_TAG_QUERY";
|
||||
payload: {
|
||||
tag: string;
|
||||
};
|
||||
}
|
||||
|
||||
interface SetFromAndToQueryAction {
|
||||
type: "SET_DURATION_QUERY";
|
||||
payload: {
|
||||
duration: Duration | null;
|
||||
};
|
||||
}
|
||||
|
||||
interface SetTypeAction {
|
||||
type: "SET_TYPE";
|
||||
payload: {
|
||||
type: MemoSpecType | "";
|
||||
};
|
||||
}
|
||||
|
||||
interface SetTextAction {
|
||||
type: "SET_TEXT";
|
||||
payload: {
|
||||
text: string;
|
||||
};
|
||||
}
|
||||
|
||||
interface SetHashAction {
|
||||
type: "SET_HASH";
|
||||
payload: {
|
||||
hash: string;
|
||||
};
|
||||
}
|
||||
|
||||
export type Actions =
|
||||
| SetLocationAction
|
||||
| SetPathnameAction
|
||||
| SetQueryAction
|
||||
| SetTagQueryAction
|
||||
| SetFromAndToQueryAction
|
||||
| SetTypeAction
|
||||
| SetTextAction
|
||||
| SetShortcutIdAction
|
||||
| SetHashAction;
|
||||
|
||||
export function reducer(state: State, action: Actions) {
|
||||
switch (action.type) {
|
||||
case "SET_LOCATION": {
|
||||
return action.payload;
|
||||
}
|
||||
case "SET_PATHNAME": {
|
||||
if (action.payload.pathname === state.pathname) {
|
||||
return state;
|
||||
}
|
||||
|
||||
return {
|
||||
...state,
|
||||
pathname: action.payload.pathname,
|
||||
};
|
||||
}
|
||||
case "SET_HASH": {
|
||||
if (action.payload.hash === state.hash) {
|
||||
return state;
|
||||
}
|
||||
|
||||
return {
|
||||
...state,
|
||||
hash: action.payload.hash,
|
||||
};
|
||||
}
|
||||
case "SET_QUERY": {
|
||||
return {
|
||||
...state,
|
||||
query: {
|
||||
...action.payload,
|
||||
},
|
||||
};
|
||||
}
|
||||
case "SET_TAG_QUERY": {
|
||||
if (action.payload.tag === state.query.tag) {
|
||||
return state;
|
||||
}
|
||||
|
||||
return {
|
||||
...state,
|
||||
query: {
|
||||
...state.query,
|
||||
tag: action.payload.tag,
|
||||
},
|
||||
};
|
||||
}
|
||||
case "SET_DURATION_QUERY": {
|
||||
if (action.payload.duration === state.query.duration) {
|
||||
return state;
|
||||
}
|
||||
|
||||
return {
|
||||
...state,
|
||||
query: {
|
||||
...state.query,
|
||||
duration: {
|
||||
...state.query.duration,
|
||||
...action.payload.duration,
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
case "SET_TYPE": {
|
||||
if (action.payload.type === state.query.type) {
|
||||
return state;
|
||||
}
|
||||
|
||||
return {
|
||||
...state,
|
||||
query: {
|
||||
...state.query,
|
||||
type: action.payload.type,
|
||||
},
|
||||
};
|
||||
}
|
||||
case "SET_TEXT": {
|
||||
if (action.payload.text === state.query.text) {
|
||||
return state;
|
||||
}
|
||||
|
||||
return {
|
||||
...state,
|
||||
query: {
|
||||
...state.query,
|
||||
text: action.payload.text,
|
||||
},
|
||||
};
|
||||
}
|
||||
case "SET_SHORTCUT_ID": {
|
||||
if (action.payload === state.query.shortcutId) {
|
||||
return state;
|
||||
}
|
||||
|
||||
return {
|
||||
...state,
|
||||
query: {
|
||||
...state.query,
|
||||
shortcutId: action.payload,
|
||||
},
|
||||
};
|
||||
}
|
||||
default: {
|
||||
return state;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const defaultState: State = {
|
||||
pathname: "/",
|
||||
hash: "",
|
||||
query: {
|
||||
tag: "",
|
||||
duration: null,
|
||||
type: "",
|
||||
text: "",
|
||||
},
|
||||
};
|
@ -1,98 +0,0 @@
|
||||
import utils from "../helpers/utils";
|
||||
|
||||
export interface State {
|
||||
memos: Memo[];
|
||||
tags: string[];
|
||||
}
|
||||
|
||||
interface SetMemosAction {
|
||||
type: "SET_MEMOS";
|
||||
payload: {
|
||||
memos: Memo[];
|
||||
};
|
||||
}
|
||||
|
||||
interface SetTagsAction {
|
||||
type: "SET_TAGS";
|
||||
payload: {
|
||||
tags: string[];
|
||||
};
|
||||
}
|
||||
|
||||
interface InsertMemoAction {
|
||||
type: "INSERT_MEMO";
|
||||
payload: {
|
||||
memo: Memo;
|
||||
};
|
||||
}
|
||||
|
||||
interface DeleteMemoByIdAction {
|
||||
type: "DELETE_MEMO_BY_ID";
|
||||
payload: {
|
||||
id: MemoId;
|
||||
};
|
||||
}
|
||||
|
||||
interface EditMemoByIdAction {
|
||||
type: "EDIT_MEMO";
|
||||
payload: Memo;
|
||||
}
|
||||
|
||||
export type Actions = SetMemosAction | SetTagsAction | InsertMemoAction | DeleteMemoByIdAction | EditMemoByIdAction;
|
||||
|
||||
export function reducer(state: State, action: Actions): State {
|
||||
switch (action.type) {
|
||||
case "SET_MEMOS": {
|
||||
const memos = utils.dedupeObjectWithId(action.payload.memos.sort((a, b) => b.createdTs - a.createdTs));
|
||||
|
||||
return {
|
||||
...state,
|
||||
memos: [...memos],
|
||||
};
|
||||
}
|
||||
case "SET_TAGS": {
|
||||
return {
|
||||
...state,
|
||||
tags: action.payload.tags,
|
||||
};
|
||||
}
|
||||
case "INSERT_MEMO": {
|
||||
const memos = utils.dedupeObjectWithId([action.payload.memo, ...state.memos].sort((a, b) => b.createdTs - a.createdTs));
|
||||
return {
|
||||
...state,
|
||||
memos,
|
||||
};
|
||||
}
|
||||
case "DELETE_MEMO_BY_ID": {
|
||||
return {
|
||||
...state,
|
||||
memos: [...state.memos].filter((memo) => memo.id !== action.payload.id),
|
||||
};
|
||||
}
|
||||
case "EDIT_MEMO": {
|
||||
const memos = state.memos.map((m) => {
|
||||
if (m.id === action.payload.id) {
|
||||
return {
|
||||
...m,
|
||||
...action.payload,
|
||||
};
|
||||
} else {
|
||||
return m;
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
...state,
|
||||
memos,
|
||||
};
|
||||
}
|
||||
default: {
|
||||
return state;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const defaultState: State = {
|
||||
memos: [],
|
||||
tags: [],
|
||||
};
|
@ -1,92 +0,0 @@
|
||||
import utils from "../helpers/utils";
|
||||
|
||||
export interface State {
|
||||
shortcuts: Shortcut[];
|
||||
}
|
||||
|
||||
interface SetShortcutsAction {
|
||||
type: "SET_SHORTCUTS";
|
||||
payload: {
|
||||
shortcuts: Shortcut[];
|
||||
};
|
||||
}
|
||||
|
||||
interface InsertShortcutAction {
|
||||
type: "INSERT_SHORTCUT";
|
||||
payload: {
|
||||
shortcut: Shortcut;
|
||||
};
|
||||
}
|
||||
|
||||
interface DeleteShortcutByIdAction {
|
||||
type: "DELETE_SHORTCUT_BY_ID";
|
||||
payload: {
|
||||
id: ShortcutId;
|
||||
};
|
||||
}
|
||||
|
||||
interface UpdateShortcutAction {
|
||||
type: "UPDATE_SHORTCUT";
|
||||
payload: Shortcut;
|
||||
}
|
||||
|
||||
export type Actions = SetShortcutsAction | InsertShortcutAction | DeleteShortcutByIdAction | UpdateShortcutAction;
|
||||
|
||||
export function reducer(state: State, action: Actions): State {
|
||||
switch (action.type) {
|
||||
case "SET_SHORTCUTS": {
|
||||
const shortcuts = utils.dedupeObjectWithId(
|
||||
action.payload.shortcuts
|
||||
.sort((a, b) => utils.getTimeStampByDate(b.createdTs) - utils.getTimeStampByDate(a.createdTs))
|
||||
.sort((a, b) => utils.getTimeStampByDate(b.updatedTs) - utils.getTimeStampByDate(a.updatedTs))
|
||||
);
|
||||
|
||||
return {
|
||||
...state,
|
||||
shortcuts,
|
||||
};
|
||||
}
|
||||
case "INSERT_SHORTCUT": {
|
||||
const shortcuts = utils.dedupeObjectWithId(
|
||||
[action.payload.shortcut, ...state.shortcuts].sort(
|
||||
(a, b) => utils.getTimeStampByDate(b.createdTs) - utils.getTimeStampByDate(a.createdTs)
|
||||
)
|
||||
);
|
||||
|
||||
return {
|
||||
...state,
|
||||
shortcuts,
|
||||
};
|
||||
}
|
||||
case "DELETE_SHORTCUT_BY_ID": {
|
||||
return {
|
||||
...state,
|
||||
shortcuts: [...state.shortcuts].filter((shortcut) => shortcut.id !== action.payload.id),
|
||||
};
|
||||
}
|
||||
case "UPDATE_SHORTCUT": {
|
||||
const shortcuts = state.shortcuts.map((m) => {
|
||||
if (m.id === action.payload.id) {
|
||||
return {
|
||||
...m,
|
||||
...action.payload,
|
||||
};
|
||||
} else {
|
||||
return m;
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
...state,
|
||||
shortcuts,
|
||||
};
|
||||
}
|
||||
default: {
|
||||
return state;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const defaultState: State = {
|
||||
shortcuts: [],
|
||||
};
|
@ -1,52 +0,0 @@
|
||||
export interface State {
|
||||
user: User | null;
|
||||
}
|
||||
|
||||
interface SignInAction {
|
||||
type: "LOGIN";
|
||||
payload: State;
|
||||
}
|
||||
|
||||
interface SignOutAction {
|
||||
type: "SIGN_OUT";
|
||||
payload: null;
|
||||
}
|
||||
|
||||
interface ResetOpenIdAction {
|
||||
type: "RESET_OPENID";
|
||||
payload: string;
|
||||
}
|
||||
|
||||
export type Actions = SignInAction | SignOutAction | ResetOpenIdAction;
|
||||
|
||||
export function reducer(state: State, action: Actions): State {
|
||||
switch (action.type) {
|
||||
case "LOGIN": {
|
||||
return {
|
||||
user: action.payload.user,
|
||||
};
|
||||
}
|
||||
case "SIGN_OUT": {
|
||||
return {
|
||||
user: null,
|
||||
};
|
||||
}
|
||||
case "RESET_OPENID": {
|
||||
if (!state.user) {
|
||||
return state;
|
||||
}
|
||||
|
||||
return {
|
||||
user: {
|
||||
...state.user,
|
||||
openId: action.payload,
|
||||
},
|
||||
};
|
||||
}
|
||||
default: {
|
||||
return state;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const defaultState: State = { user: null };
|
Loading…
Reference in New Issue