@knapsack/app
Version:
Build Design Systems on top of knapsack, by Basalt
177 lines (161 loc) • 4.38 kB
text/typescript
import { ThunkAction, ThunkMiddleware } from 'redux-thunk';
import { Action } from './types';
import { setStatus } from './ui';
import { getUserInfo } from '../../cloud/user-utils';
import { apiUrlBase } from '../../lib/constants';
import { KnapsackDataStoreSaveBody } from '../../schemas/misc';
type AppState = import('./index').AppState;
const SAVE_TO_SERVER_REQUEST = 'knapsack/meta/SAVE_TO_SERVER_REQUEST';
const SAVE_TO_SERVER_SUCCESS = 'knapsack/meta/SAVE_TO_SERVER_SUCCESS';
const SAVE_TO_SERVER_FAIL = 'knapsack/meta/SAVE_TO_SERVER_FAIL';
export interface MetaState {
meta?: import('../../schemas/misc').KnapsackMeta;
plugins: {
id: string;
/**
* If `loadContent()` hook was implemented, then an API endpoint is set up and waiting for us
*/
hasContent: boolean;
clientPluginPath?: string;
}[];
}
const initialState: MetaState = {
plugins: [],
};
interface SaveToServerRequestAction extends Action {
type: typeof SAVE_TO_SERVER_REQUEST;
}
interface SaveToServerSuccessAction extends Action {
type: typeof SAVE_TO_SERVER_SUCCESS;
}
interface SaveToServerFailAction extends Action {
type: typeof SAVE_TO_SERVER_FAIL;
}
type Actions =
| SaveToServerRequestAction
| SaveToServerSuccessAction
| SaveToServerFailAction;
export function saveToServer({
storageLocation,
title,
message,
}: Omit<KnapsackDataStoreSaveBody, 'state'>): ThunkAction<
void,
AppState,
{},
Actions
> {
const showStatusMsgs = storageLocation === 'cloud';
return async (dispatch, getState) => {
dispatch({ type: SAVE_TO_SERVER_REQUEST });
if (showStatusMsgs) {
dispatch(
setStatus({
message: 'Saving...',
type: 'info',
}),
);
}
const state = getState();
const body: KnapsackDataStoreSaveBody = {
storageLocation,
state,
title,
message,
};
const { user, role, ksRepoAccess, token, username } = await getUserInfo();
const userHeaders = user
? {
Authorization: token ? `token ${token}` : null,
'x-ks-cloud-role-id': role?.id,
'x-ks-cloud-repo-access': ksRepoAccess?.join(','),
'x-ks-cloud-username': username,
}
: {};
return window
.fetch(`${apiUrlBase}/data-store/`, {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
...userHeaders,
},
body: JSON.stringify(body),
})
.then(res => res.json())
.then(async results => {
if (!results.ok) {
console.error('failed saveToServer', results);
dispatch({ type: SAVE_TO_SERVER_FAIL });
dispatch(
setStatus({
message: `Failed: ${results.message}`,
type: 'error',
dismissAfter: 30,
}),
);
} else {
dispatch({ type: SAVE_TO_SERVER_SUCCESS });
if (showStatusMsgs) {
dispatch(
setStatus({
message: `Success: ${results.message}`,
type: 'success',
dismissAfter: 45,
}),
);
}
}
return results;
});
};
}
export default function reducer(
state = initialState,
action: Actions,
): MetaState {
switch (action.type) {
default:
return {
...initialState,
...state,
};
}
}
let timeoutId: any;
export const autoSaveMiddleware: ThunkMiddleware<
AppState,
Actions
> = store => next => (action: Actions) => {
const ignoredActionBases = ['knapsack/ui', 'knapsack/meta', 'knapsack/user'];
if (ignoredActionBases.some(x => action.type.startsWith(x))) {
return next(action);
}
const {
isLocalDev,
features: { autosave },
} = store.getState().userState;
if (!isLocalDev || !autosave) {
return next(action);
}
/**
* Ms after last action to autosave, if enabled
*/
const autosaveDelay = action.meta?.autosaveDelay ?? 1500;
if (autosaveDelay === -1) {
// console.log('skipping');
return next(action);
}
if (timeoutId) {
clearTimeout(timeoutId);
}
timeoutId = setTimeout(() => {
store.dispatch(
saveToServer({
storageLocation: 'local',
}),
);
timeoutId = '';
}, autosaveDelay);
return next(action);
};