mirror of
https://github.com/strapi/strapi.git
synced 2025-12-30 00:37:24 +00:00
Created useAppInfos hook
Signed-off-by: soupette <cyril.lpz@gmail.com>
This commit is contained in:
parent
c420370c9d
commit
4db2e962d1
@ -0,0 +1,44 @@
|
||||
import React from 'react';
|
||||
import { LoadingIndicatorPage, AppInfosContext } from '@strapi/helper-plugin';
|
||||
import { useQueries } from 'react-query';
|
||||
import PluginsInitializer from '../PluginsInitializer';
|
||||
import RBACProvider from '../RBACProvider';
|
||||
import { fetchAppInfo, fetchCurrentUserPermissions } from './utils/api';
|
||||
|
||||
const AuthenticatedApp = () => {
|
||||
// TODO: clean components that depends on this
|
||||
// This part is just to prepare the refactoring of the Admin page
|
||||
const [
|
||||
{ data: appInfos, status },
|
||||
{ data: permissions, status: fetchPermissionsStatus, refetch, isFetched, isFetching },
|
||||
] = useQueries([
|
||||
{ queryKey: 'app-infos', queryFn: fetchAppInfo },
|
||||
|
||||
{
|
||||
queryKey: 'admin-users-permission',
|
||||
queryFn: fetchCurrentUserPermissions,
|
||||
},
|
||||
]);
|
||||
|
||||
const shouldShowNotDependentQueriesLoader =
|
||||
(isFetching && isFetched) || status === 'loading' || fetchPermissionsStatus === 'loading';
|
||||
|
||||
if (shouldShowNotDependentQueriesLoader) {
|
||||
return <LoadingIndicatorPage />;
|
||||
}
|
||||
|
||||
// TODO add error state
|
||||
if (status === 'error') {
|
||||
return <div>error...</div>;
|
||||
}
|
||||
|
||||
return (
|
||||
<AppInfosContext.Provider value={appInfos}>
|
||||
<RBACProvider permissions={permissions} refetchPermissions={refetch}>
|
||||
<PluginsInitializer />
|
||||
</RBACProvider>
|
||||
</AppInfosContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
export default AuthenticatedApp;
|
||||
@ -0,0 +1,31 @@
|
||||
import axiosInstance from '../../../utils/axiosInstance';
|
||||
|
||||
const fetchAppInfo = async () => {
|
||||
try {
|
||||
const { data, headers } = await axiosInstance.get('/admin/information');
|
||||
|
||||
if (!headers['content-type'].includes('application/json')) {
|
||||
throw new Error('Not found');
|
||||
}
|
||||
|
||||
return data.data;
|
||||
} catch (error) {
|
||||
throw new Error(error);
|
||||
}
|
||||
};
|
||||
|
||||
const fetchCurrentUserPermissions = async () => {
|
||||
try {
|
||||
const { data, headers } = await axiosInstance.get('/admin/users/me/permissions');
|
||||
|
||||
if (!headers['content-type'].includes('application/json')) {
|
||||
throw new Error('Not found');
|
||||
}
|
||||
|
||||
return data.data;
|
||||
} catch (err) {
|
||||
throw new Error(err);
|
||||
}
|
||||
};
|
||||
|
||||
export { fetchAppInfo, fetchCurrentUserPermissions };
|
||||
@ -5,11 +5,13 @@
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { PropTypes } from 'prop-types';
|
||||
|
||||
import { useAppInfos } from '@strapi/helper-plugin';
|
||||
import Wrapper, { A } from './Wrapper';
|
||||
|
||||
function LeftMenuFooter({ version }) {
|
||||
function LeftMenuFooter() {
|
||||
const projectType = process.env.STRAPI_ADMIN_PROJECT_TYPE;
|
||||
const { strapiVersion } = useAppInfos();
|
||||
|
||||
return (
|
||||
<Wrapper>
|
||||
@ -19,12 +21,12 @@ function LeftMenuFooter({ version }) {
|
||||
</A>
|
||||
|
||||
<A
|
||||
href={`https://github.com/strapi/strapi/releases/tag/v${version}`}
|
||||
href={`https://github.com/strapi/strapi/releases/tag/v${strapiVersion}`}
|
||||
key="github"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
v{version}
|
||||
v{strapiVersion}
|
||||
</A>
|
||||
|
||||
<A href="https://strapi.io" target="_blank" rel="noopener noreferrer">
|
||||
@ -35,8 +37,4 @@ function LeftMenuFooter({ version }) {
|
||||
);
|
||||
}
|
||||
|
||||
LeftMenuFooter.propTypes = {
|
||||
version: PropTypes.string.isRequired,
|
||||
};
|
||||
|
||||
export default LeftMenuFooter;
|
||||
|
||||
@ -6,7 +6,7 @@ import { Footer, Header, Loader, LinksContainer, LinksSection } from './compos';
|
||||
import Wrapper from './Wrapper';
|
||||
import useMenuSections from './useMenuSections';
|
||||
|
||||
const LeftMenu = ({ shouldUpdateStrapi, version, plugins, setUpdateMenu }) => {
|
||||
const LeftMenu = ({ shouldUpdateStrapi, plugins, setUpdateMenu }) => {
|
||||
const location = useLocation();
|
||||
|
||||
const {
|
||||
@ -81,14 +81,13 @@ const LeftMenu = ({ shouldUpdateStrapi, version, plugins, setUpdateMenu }) => {
|
||||
/>
|
||||
)}
|
||||
</LinksContainer>
|
||||
<Footer key="footer" version={version} />
|
||||
<Footer key="footer" />
|
||||
</Wrapper>
|
||||
);
|
||||
};
|
||||
|
||||
LeftMenu.propTypes = {
|
||||
shouldUpdateStrapi: PropTypes.bool.isRequired,
|
||||
version: PropTypes.string.isRequired,
|
||||
plugins: PropTypes.object.isRequired,
|
||||
setUpdateMenu: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
@ -1,12 +1,12 @@
|
||||
import React, { useReducer, useRef } from 'react';
|
||||
import { LoadingIndicatorPage, useStrapiApp } from '@strapi/helper-plugin';
|
||||
import Admin from '../../pages/Admin';
|
||||
import ReleaseNotification from '../ReleaseNotification';
|
||||
import init from './init';
|
||||
import reducer, { initialState } from './reducer';
|
||||
|
||||
const PluginsInitializer = () => {
|
||||
const { plugins: appPlugins } = useStrapiApp();
|
||||
|
||||
const [{ plugins }, dispatch] = useReducer(reducer, initialState, () => init(appPlugins));
|
||||
const setPlugin = useRef(pluginId => {
|
||||
dispatch({ type: 'SET_PLUGIN_READY', pluginId });
|
||||
@ -35,7 +35,12 @@ const PluginsInitializer = () => {
|
||||
);
|
||||
}
|
||||
|
||||
return <Admin plugins={plugins} />;
|
||||
return (
|
||||
<>
|
||||
<ReleaseNotification />
|
||||
<Admin plugins={plugins} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default PluginsInitializer;
|
||||
|
||||
@ -32,7 +32,7 @@ const PrivateRoute = ({ component: Component, path, ...rest }) => (
|
||||
);
|
||||
|
||||
PrivateRoute.propTypes = {
|
||||
component: PropTypes.oneOfType([PropTypes.node, PropTypes.func]).isRequired,
|
||||
component: PropTypes.oneOfType([PropTypes.node, PropTypes.func, PropTypes.element]).isRequired,
|
||||
path: PropTypes.string.isRequired,
|
||||
};
|
||||
|
||||
|
||||
@ -0,0 +1,10 @@
|
||||
import { RESET_STORE, SET_PERMISSIONS } from './constants';
|
||||
|
||||
const resetStore = () => ({ type: RESET_STORE });
|
||||
|
||||
const setPermissions = permissions => ({
|
||||
type: SET_PERMISSIONS,
|
||||
permissions,
|
||||
});
|
||||
|
||||
export { resetStore, setPermissions };
|
||||
@ -0,0 +1,2 @@
|
||||
export const RESET_STORE = 'StrapiAdmin/RBACProvider/RESET_STORE';
|
||||
export const SET_PERMISSIONS = 'StrapiAdmin/RBACProvider/SET_PERMISSIONS';
|
||||
@ -0,0 +1,35 @@
|
||||
import React, { createContext, useEffect } from 'react';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import { LoadingIndicatorPage } from '@strapi/helper-plugin';
|
||||
import PropTypes from 'prop-types';
|
||||
import { resetStore, setPermissions } from './actions';
|
||||
|
||||
export const C = createContext();
|
||||
|
||||
const RBACProvider = ({ children, permissions, refetchPermissions }) => {
|
||||
const { allPermissions } = useSelector(state => state.rbacProvider);
|
||||
const dispatch = useDispatch();
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(setPermissions(permissions));
|
||||
|
||||
return () => {
|
||||
console.log('up');
|
||||
dispatch(resetStore());
|
||||
};
|
||||
}, [permissions, dispatch]);
|
||||
|
||||
if (!allPermissions) {
|
||||
return <LoadingIndicatorPage />;
|
||||
}
|
||||
|
||||
return <C.Provider value={refetchPermissions}>{children}</C.Provider>;
|
||||
};
|
||||
|
||||
RBACProvider.propTypes = {
|
||||
children: PropTypes.element.isRequired,
|
||||
permissions: PropTypes.array.isRequired,
|
||||
refetchPermissions: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
export default RBACProvider;
|
||||
@ -0,0 +1,53 @@
|
||||
/*
|
||||
*
|
||||
* RBACProvider reducer
|
||||
* The goal of this reducer is to provide
|
||||
* the plugins with an access to the user's permissions
|
||||
* in our middleware system
|
||||
*
|
||||
*/
|
||||
|
||||
import produce from 'immer';
|
||||
|
||||
import { RESET_STORE, SET_PERMISSIONS } from './constants';
|
||||
|
||||
const initialState = {
|
||||
allPermissions: null,
|
||||
adminPermissions: {},
|
||||
collectionTypesRelatedPermissions: {},
|
||||
pluginsPermissions: {},
|
||||
};
|
||||
|
||||
const reducer = (state = initialState, action) =>
|
||||
// eslint-disable-next-line consistent-return
|
||||
produce(state, draftState => {
|
||||
switch (action.type) {
|
||||
case SET_PERMISSIONS: {
|
||||
draftState.allPermissions = action.permissions;
|
||||
draftState.collectionTypesRelatedPermissions = action.permissions
|
||||
.filter(perm => perm.subject)
|
||||
.reduce((acc, current) => {
|
||||
const { subject, action } = current;
|
||||
|
||||
if (!acc[subject]) {
|
||||
acc[subject] = {};
|
||||
}
|
||||
|
||||
acc[subject] = acc[subject][action]
|
||||
? { ...acc[subject], [action]: [...acc[subject][action], current] }
|
||||
: { ...acc[subject], [action]: [current] };
|
||||
|
||||
return acc;
|
||||
}, {});
|
||||
break;
|
||||
}
|
||||
case RESET_STORE: {
|
||||
return initialState;
|
||||
}
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
});
|
||||
|
||||
export default reducer;
|
||||
export { initialState };
|
||||
@ -0,0 +1,44 @@
|
||||
import { useEffect } from 'react';
|
||||
import { useQuery } from 'react-query';
|
||||
import { useAppInfos, useNotification } from '@strapi/helper-plugin';
|
||||
import checkLatestStrapiVersion from './utils/checkLatestStrapiVersion';
|
||||
import fetchStrapiLatestRelease from './utils/api';
|
||||
|
||||
const { STRAPI_ADMIN_UPDATE_NOTIFICATION } = process.env;
|
||||
const canFetchRelease = STRAPI_ADMIN_UPDATE_NOTIFICATION === 'false';
|
||||
const showUpdateNotif = !JSON.parse(localStorage.getItem('STRAPI_UPDATE_NOTIF'));
|
||||
|
||||
const ReleaseNotification = () => {
|
||||
const { strapiVersion } = useAppInfos();
|
||||
const toggleNotification = useNotification();
|
||||
const { data: tag_name, status } = useQuery({
|
||||
queryKey: 'strapi-release',
|
||||
queryFn: fetchStrapiLatestRelease,
|
||||
enabled: canFetchRelease,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (status === 'success' && showUpdateNotif) {
|
||||
const shouldUpdateStrapi = checkLatestStrapiVersion(strapiVersion, tag_name);
|
||||
|
||||
if (shouldUpdateStrapi) {
|
||||
toggleNotification({
|
||||
type: 'info',
|
||||
message: { id: 'notification.version.update.message' },
|
||||
link: {
|
||||
url: `https://github.com/strapi/strapi/releases/tag/${tag_name}`,
|
||||
label: {
|
||||
id: 'notification.version.update.link',
|
||||
},
|
||||
},
|
||||
blockTransition: true,
|
||||
onClose: () => localStorage.setItem('STRAPI_UPDATE_NOTIF', true),
|
||||
});
|
||||
}
|
||||
}
|
||||
}, [status, tag_name, strapiVersion, toggleNotification]);
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
export default ReleaseNotification;
|
||||
@ -0,0 +1,19 @@
|
||||
import axios from 'axios';
|
||||
import packageJSON from '../../../../../package.json';
|
||||
|
||||
const strapiVersion = packageJSON.version;
|
||||
|
||||
const fetchStrapiLatestRelease = async () => {
|
||||
try {
|
||||
const {
|
||||
data: { tag_name },
|
||||
} = await axios.get('https://api.github.com/repos/strapi/strapi/releases/latest');
|
||||
|
||||
return tag_name;
|
||||
} catch (err) {
|
||||
// Don't throw an error
|
||||
return strapiVersion;
|
||||
}
|
||||
};
|
||||
|
||||
export default fetchStrapiLatestRelease;
|
||||
@ -21,7 +21,6 @@ import {
|
||||
request,
|
||||
NotificationsContext,
|
||||
} from '@strapi/helper-plugin';
|
||||
import { checkLatestStrapiVersion } from '../../utils';
|
||||
|
||||
import adminPermissions from '../../permissions';
|
||||
import Header from '../../components/Header/index';
|
||||
@ -110,50 +109,8 @@ export class Admin extends React.Component {
|
||||
}
|
||||
};
|
||||
|
||||
fetchStrapiLatestRelease = async () => {
|
||||
const {
|
||||
global: { strapiVersion },
|
||||
getStrapiLatestReleaseSucceeded,
|
||||
} = this.props;
|
||||
|
||||
if (process.env.STRAPI_ADMIN_UPDATE_NOTIFICATION === 'true') {
|
||||
try {
|
||||
const {
|
||||
data: { tag_name },
|
||||
} = await axios.get('https://api.github.com/repos/strapi/strapi/releases/latest');
|
||||
const shouldUpdateStrapi = checkLatestStrapiVersion(strapiVersion, tag_name);
|
||||
|
||||
getStrapiLatestReleaseSucceeded(tag_name, shouldUpdateStrapi);
|
||||
|
||||
const showUpdateNotif = !JSON.parse(localStorage.getItem('STRAPI_UPDATE_NOTIF'));
|
||||
|
||||
if (!showUpdateNotif) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (shouldUpdateStrapi) {
|
||||
this.context.toggleNotification({
|
||||
type: 'info',
|
||||
message: { id: 'notification.version.update.message' },
|
||||
link: {
|
||||
url: `https://github.com/strapi/strapi/releases/tag/${tag_name}`,
|
||||
label: {
|
||||
id: 'notification.version.update.link',
|
||||
},
|
||||
},
|
||||
blockTransition: true,
|
||||
onClose: () => localStorage.setItem('STRAPI_UPDATE_NOTIF', true),
|
||||
});
|
||||
}
|
||||
} catch (err) {
|
||||
// Silent
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
initApp = async () => {
|
||||
await this.fetchAppInfo();
|
||||
await this.fetchStrapiLatestRelease();
|
||||
};
|
||||
|
||||
renderPluginDispatcher = props => {
|
||||
@ -194,7 +151,6 @@ export class Admin extends React.Component {
|
||||
<Wrapper>
|
||||
<LeftMenu
|
||||
shouldUpdateStrapi={shouldUpdateStrapi}
|
||||
version={strapiVersion}
|
||||
plugins={plugins}
|
||||
setUpdateMenu={this.setUpdateMenu}
|
||||
/>
|
||||
|
||||
@ -10,7 +10,7 @@ import { Switch, Route } from 'react-router-dom';
|
||||
import { connect } from 'react-redux';
|
||||
import { bindActionCreators, compose } from 'redux';
|
||||
import { LoadingIndicatorPage, auth, request, useNotification } from '@strapi/helper-plugin';
|
||||
import PluginsInitializer from '../../components/PluginsInitializer';
|
||||
import AuthenticatedApp from '../../components/AuthenticatedApp';
|
||||
import PrivateRoute from '../../components/PrivateRoute';
|
||||
import AuthPage from '../AuthPage';
|
||||
import NotFoundPage from '../NotFoundPage';
|
||||
@ -20,10 +20,6 @@ import { getDataSucceeded } from './actions';
|
||||
import routes from './utils/routes';
|
||||
import { makeUniqueRoutes, createRoute } from '../SettingsPage/utils';
|
||||
|
||||
window.strapi = Object.assign(window.strapi || {}, {
|
||||
lockAppWithOverlay: () => console.log('todo unlockAppWithOverlay'),
|
||||
});
|
||||
|
||||
function App(props) {
|
||||
const toggleNotification = useNotification();
|
||||
const getDataRef = useRef();
|
||||
@ -118,7 +114,7 @@ function App(props) {
|
||||
)}
|
||||
exact
|
||||
/>
|
||||
<PrivateRoute path="/" component={PluginsInitializer} />
|
||||
<PrivateRoute path="/" component={AuthenticatedApp} />
|
||||
<Route path="" component={NotFoundPage} />
|
||||
</Switch>
|
||||
</Content>
|
||||
|
||||
@ -3,6 +3,7 @@ import adminReducer from './pages/Admin/reducer';
|
||||
import languageProviderReducer from './components/LanguageProvider/reducer';
|
||||
import menuReducer from './components/LeftMenu/reducer';
|
||||
import permissionsManagerReducer from './components/PermissionsManager/reducer';
|
||||
import rbacProviderReducer from './components/RBACProvider/reducer';
|
||||
|
||||
const reducers = {
|
||||
admin: adminReducer,
|
||||
@ -10,6 +11,7 @@ const reducers = {
|
||||
language: languageProviderReducer,
|
||||
menu: menuReducer,
|
||||
permissionsManager: permissionsManagerReducer,
|
||||
rbacProvider: rbacProviderReducer,
|
||||
};
|
||||
|
||||
export default reducers;
|
||||
|
||||
13
packages/core/admin/admin/src/utils/axiosInstance.js
Normal file
13
packages/core/admin/admin/src/utils/axiosInstance.js
Normal file
@ -0,0 +1,13 @@
|
||||
import axios from 'axios';
|
||||
import { auth } from '@strapi/helper-plugin';
|
||||
|
||||
const instance = axios.create({
|
||||
baseURL: process.env.STRAPI_ADMIN_BACKEND_URL,
|
||||
timeout: 1000,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Bearer ${auth.getToken()}`,
|
||||
},
|
||||
});
|
||||
|
||||
export default instance;
|
||||
@ -5,5 +5,5 @@ export { default as retrieveGlobalLinks } from './retrieveGlobalLinks';
|
||||
export { default as retrievePluginsMenu } from './retrievePluginsMenu';
|
||||
export { default as sortLinks } from './sortLinks';
|
||||
export { default as getExistingActions } from './getExistingActions';
|
||||
export { default as checkLatestStrapiVersion } from './checkLatestStrapiVersion';
|
||||
|
||||
export { default as getRequestUrl } from './getRequestUrl';
|
||||
|
||||
@ -0,0 +1,12 @@
|
||||
/**
|
||||
*
|
||||
* AppInfosContext
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
import { createContext } from 'react';
|
||||
|
||||
const AppInfosContext = createContext();
|
||||
|
||||
export default AppInfosContext;
|
||||
@ -0,0 +1,16 @@
|
||||
/**
|
||||
*
|
||||
* useAppInfos
|
||||
*
|
||||
*/
|
||||
|
||||
import { useContext } from 'react';
|
||||
import AppInfosContext from '../../contexts/AppInfosContext';
|
||||
|
||||
const useAppInfos = () => {
|
||||
const appInfos = useContext(AppInfosContext);
|
||||
|
||||
return appInfos;
|
||||
};
|
||||
|
||||
export default useAppInfos;
|
||||
@ -106,6 +106,7 @@ export { default as PopUpWarningIcon } from './components/PopUpWarning/Icon';
|
||||
export { default as PopUpWarningModal } from './components/PopUpWarning/StyledModal';
|
||||
|
||||
// Contexts
|
||||
export { default as AppInfosContext } from './contexts/AppInfosContext';
|
||||
export { default as AutoReloadOverlayBockerContext } from './contexts/AutoReloadOverlayBockerContext';
|
||||
export { default as NotificationsContext } from './contexts/NotificationsContext';
|
||||
export { default as OverlayBlockerContext } from './contexts/OverlayBlockerContext';
|
||||
@ -114,6 +115,7 @@ export { default as UserContext } from './contexts/UserContext';
|
||||
export { default as ContentManagerEditViewDataManagerContext } from './contexts/ContentManagerEditViewDataManagerContext';
|
||||
|
||||
// Hooks
|
||||
export { default as useAppInfos } from './hooks/useAppInfos';
|
||||
export { default as useContentManagerEditViewDataManager } from './hooks/useContentManagerEditViewDataManager';
|
||||
export { default as useQuery } from './hooks/useQuery';
|
||||
export { default as useLibrary } from './hooks/useLibrary';
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user