mirror of
https://github.com/shlinkio/shlink-web-client.git
synced 2024-12-23 09:30:31 +03:00
Added banner to be displayed when the service worker has updated the app in the background
This commit is contained in:
parent
4546b74b6f
commit
5caa648112
9 changed files with 90 additions and 20 deletions
16
src/App.scss
16
src/App.scss
|
@ -1,4 +1,5 @@
|
||||||
@import './utils/base';
|
@import './utils/base';
|
||||||
|
@import './utils/mixins/horizontal-align';
|
||||||
|
|
||||||
.app-container {
|
.app-container {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
@ -24,3 +25,18 @@
|
||||||
padding: 0 15px;
|
padding: 0 15px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.app__update-banner.app__update-banner {
|
||||||
|
@include horizontal-align();
|
||||||
|
|
||||||
|
position: fixed;
|
||||||
|
top: $headerHeight - 25px;
|
||||||
|
padding: 0 4rem 0 0;
|
||||||
|
z-index: 1040;
|
||||||
|
margin: 0;
|
||||||
|
color: var(--text-color);
|
||||||
|
text-align: center;
|
||||||
|
width: 700px;
|
||||||
|
max-width: calc(100% - 30px);
|
||||||
|
box-shadow: 0 0 1rem var(--brand-color);
|
||||||
|
}
|
||||||
|
|
19
src/App.tsx
19
src/App.tsx
|
@ -1,15 +1,19 @@
|
||||||
import { useEffect, FC } from 'react';
|
import { useEffect, FC } from 'react';
|
||||||
import { Route, Switch } from 'react-router-dom';
|
import { Route, Switch } from 'react-router-dom';
|
||||||
|
import { Alert } from 'reactstrap';
|
||||||
import NotFound from './common/NotFound';
|
import NotFound from './common/NotFound';
|
||||||
import { ServersMap } from './servers/data';
|
import { ServersMap } from './servers/data';
|
||||||
import { Settings } from './settings/reducers/settings';
|
import { Settings } from './settings/reducers/settings';
|
||||||
import { changeThemeInMarkup } from './utils/theme';
|
import { changeThemeInMarkup } from './utils/theme';
|
||||||
|
import { SimpleCard } from './utils/SimpleCard';
|
||||||
import './App.scss';
|
import './App.scss';
|
||||||
|
|
||||||
interface AppProps {
|
interface AppProps {
|
||||||
fetchServers: Function;
|
fetchServers: () => void;
|
||||||
servers: ServersMap;
|
servers: ServersMap;
|
||||||
settings: Settings;
|
settings: Settings;
|
||||||
|
resetAppUpdate: () => void;
|
||||||
|
appUpdated: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const App = (
|
const App = (
|
||||||
|
@ -20,7 +24,7 @@ const App = (
|
||||||
EditServer: FC,
|
EditServer: FC,
|
||||||
Settings: FC,
|
Settings: FC,
|
||||||
ShlinkVersionsContainer: FC,
|
ShlinkVersionsContainer: FC,
|
||||||
) => ({ fetchServers, servers, settings }: AppProps) => {
|
) => ({ fetchServers, servers, settings, appUpdated, resetAppUpdate }: AppProps) => {
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// On first load, try to fetch the remote servers if the list is empty
|
// On first load, try to fetch the remote servers if the list is empty
|
||||||
if (Object.keys(servers).length === 0) {
|
if (Object.keys(servers).length === 0) {
|
||||||
|
@ -50,6 +54,17 @@ const App = (
|
||||||
<ShlinkVersionsContainer />
|
<ShlinkVersionsContainer />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<Alert
|
||||||
|
className="app__update-banner"
|
||||||
|
tag={SimpleCard}
|
||||||
|
color="secondary"
|
||||||
|
isOpen={appUpdated}
|
||||||
|
toggle={resetAppUpdate}
|
||||||
|
>
|
||||||
|
<h4 className="mb-4">This app has just been updated!</h4>
|
||||||
|
<p className="mb-0">Restart it to enjoy the new features.</p>
|
||||||
|
</Alert>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
18
src/app/reducers/appUpdates.ts
Normal file
18
src/app/reducers/appUpdates.ts
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
import { Action } from 'redux';
|
||||||
|
import { buildActionCreator, buildReducer } from '../../utils/helpers/redux';
|
||||||
|
|
||||||
|
/* eslint-disable padding-line-between-statements */
|
||||||
|
export const APP_UPDATE_AVAILABLE = 'shlink/appUpdates/APP_UPDATE_AVAILABLE';
|
||||||
|
export const RESET_APP_UPDATE = 'shlink/appUpdates/RESET_APP_UPDATE';
|
||||||
|
/* eslint-enable padding-line-between-statements */
|
||||||
|
|
||||||
|
const initialState = false;
|
||||||
|
|
||||||
|
export default buildReducer<boolean, Action<string>>({
|
||||||
|
[APP_UPDATE_AVAILABLE]: () => true,
|
||||||
|
[RESET_APP_UPDATE]: () => false,
|
||||||
|
}, initialState);
|
||||||
|
|
||||||
|
export const appUpdateAvailable = buildActionCreator(APP_UPDATE_AVAILABLE);
|
||||||
|
|
||||||
|
export const resetAppUpdate = buildActionCreator(RESET_APP_UPDATE);
|
26
src/app/services/provideServices.ts
Normal file
26
src/app/services/provideServices.ts
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
import Bottle from 'bottlejs';
|
||||||
|
import { appUpdateAvailable, resetAppUpdate } from '../reducers/appUpdates';
|
||||||
|
import App from '../../App';
|
||||||
|
import { ConnectDecorator } from '../../container/types';
|
||||||
|
|
||||||
|
const provideServices = (bottle: Bottle, connect: ConnectDecorator) => {
|
||||||
|
// Components
|
||||||
|
bottle.serviceFactory(
|
||||||
|
'App',
|
||||||
|
App,
|
||||||
|
'MainHeader',
|
||||||
|
'Home',
|
||||||
|
'MenuLayout',
|
||||||
|
'CreateServer',
|
||||||
|
'EditServer',
|
||||||
|
'Settings',
|
||||||
|
'ShlinkVersionsContainer',
|
||||||
|
);
|
||||||
|
bottle.decorator('App', connect([ 'servers', 'settings', 'appUpdated' ], [ 'fetchServers', 'resetAppUpdate' ]));
|
||||||
|
|
||||||
|
// Actions
|
||||||
|
bottle.serviceFactory('appUpdateAvailable', () => appUpdateAvailable);
|
||||||
|
bottle.serviceFactory('resetAppUpdate', () => resetAppUpdate);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default provideServices;
|
|
@ -3,9 +3,9 @@ import { Link } from 'react-router-dom';
|
||||||
import { Card, Row } from 'reactstrap';
|
import { Card, Row } from 'reactstrap';
|
||||||
import { ExternalLink } from 'react-external-link';
|
import { ExternalLink } from 'react-external-link';
|
||||||
import ServersListGroup from '../servers/ServersListGroup';
|
import ServersListGroup from '../servers/ServersListGroup';
|
||||||
import './Home.scss';
|
|
||||||
import { ServersMap } from '../servers/data';
|
import { ServersMap } from '../servers/data';
|
||||||
import { ShlinkLogo } from './img/ShlinkLogo';
|
import { ShlinkLogo } from './img/ShlinkLogo';
|
||||||
|
import './Home.scss';
|
||||||
|
|
||||||
export interface HomeProps {
|
export interface HomeProps {
|
||||||
servers: ServersMap;
|
servers: ServersMap;
|
||||||
|
|
|
@ -2,7 +2,6 @@ import Bottle, { IContainer } from 'bottlejs';
|
||||||
import { withRouter } from 'react-router-dom';
|
import { withRouter } from 'react-router-dom';
|
||||||
import { connect as reduxConnect } from 'react-redux';
|
import { connect as reduxConnect } from 'react-redux';
|
||||||
import { pick } from 'ramda';
|
import { pick } from 'ramda';
|
||||||
import App from '../App';
|
|
||||||
import provideApiServices from '../api/services/provideServices';
|
import provideApiServices from '../api/services/provideServices';
|
||||||
import provideCommonServices from '../common/services/provideServices';
|
import provideCommonServices from '../common/services/provideServices';
|
||||||
import provideShortUrlsServices from '../short-urls/services/provideServices';
|
import provideShortUrlsServices from '../short-urls/services/provideServices';
|
||||||
|
@ -13,6 +12,7 @@ import provideUtilsServices from '../utils/services/provideServices';
|
||||||
import provideMercureServices from '../mercure/services/provideServices';
|
import provideMercureServices from '../mercure/services/provideServices';
|
||||||
import provideSettingsServices from '../settings/services/provideServices';
|
import provideSettingsServices from '../settings/services/provideServices';
|
||||||
import provideDomainsServices from '../domains/services/provideServices';
|
import provideDomainsServices from '../domains/services/provideServices';
|
||||||
|
import provideAppServices from '../app/services/provideServices';
|
||||||
import { ConnectDecorator } from './types';
|
import { ConnectDecorator } from './types';
|
||||||
|
|
||||||
type LazyActionMap = Record<string, Function>;
|
type LazyActionMap = Record<string, Function>;
|
||||||
|
@ -33,19 +33,7 @@ const connect: ConnectDecorator = (propsFromState: string[] | null, actionServic
|
||||||
actionServiceNames.reduce(mapActionService, {}),
|
actionServiceNames.reduce(mapActionService, {}),
|
||||||
);
|
);
|
||||||
|
|
||||||
bottle.serviceFactory(
|
provideAppServices(bottle, connect);
|
||||||
'App',
|
|
||||||
App,
|
|
||||||
'MainHeader',
|
|
||||||
'Home',
|
|
||||||
'MenuLayout',
|
|
||||||
'CreateServer',
|
|
||||||
'EditServer',
|
|
||||||
'Settings',
|
|
||||||
'ShlinkVersionsContainer',
|
|
||||||
);
|
|
||||||
bottle.decorator('App', connect([ 'servers', 'settings' ], [ 'fetchServers' ]));
|
|
||||||
|
|
||||||
provideCommonServices(bottle, connect, withRouter);
|
provideCommonServices(bottle, connect, withRouter);
|
||||||
provideApiServices(bottle);
|
provideApiServices(bottle);
|
||||||
provideShortUrlsServices(bottle, connect);
|
provideShortUrlsServices(bottle, connect);
|
||||||
|
|
|
@ -35,6 +35,7 @@ export interface ShlinkState {
|
||||||
settings: Settings;
|
settings: Settings;
|
||||||
domainsList: DomainsList;
|
domainsList: DomainsList;
|
||||||
visitsOverview: VisitsOverview;
|
visitsOverview: VisitsOverview;
|
||||||
|
appUpdated: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ConnectDecorator = (props: string[] | null, actions?: string[]) => any;
|
export type ConnectDecorator = (props: string[] | null, actions?: string[]) => any;
|
||||||
|
|
|
@ -5,7 +5,7 @@ import { homepage } from '../package.json';
|
||||||
import container from './container';
|
import container from './container';
|
||||||
import store from './container/store';
|
import store from './container/store';
|
||||||
import { fixLeafletIcons } from './utils/helpers/leaflet';
|
import { fixLeafletIcons } from './utils/helpers/leaflet';
|
||||||
import * as serviceWorkerRegistration from './serviceWorkerRegistration';
|
import { register as registerServiceWorker } from './serviceWorkerRegistration';
|
||||||
import 'react-datepicker/dist/react-datepicker.css';
|
import 'react-datepicker/dist/react-datepicker.css';
|
||||||
import 'leaflet/dist/leaflet.css';
|
import 'leaflet/dist/leaflet.css';
|
||||||
import './index.scss';
|
import './index.scss';
|
||||||
|
@ -13,7 +13,7 @@ import './index.scss';
|
||||||
// This overwrites icons used for leaflet maps, fixing some issues caused by webpack while processing the CSS
|
// This overwrites icons used for leaflet maps, fixing some issues caused by webpack while processing the CSS
|
||||||
fixLeafletIcons();
|
fixLeafletIcons();
|
||||||
|
|
||||||
const { App, ScrollToTop, ErrorHandler } = container;
|
const { App, ScrollToTop, ErrorHandler, appUpdateAvailable } = container;
|
||||||
|
|
||||||
render(
|
render(
|
||||||
<Provider store={store}>
|
<Provider store={store}>
|
||||||
|
@ -31,4 +31,8 @@ render(
|
||||||
// If you want your app to work offline and load faster, you can change
|
// If you want your app to work offline and load faster, you can change
|
||||||
// unregister() to register() below. Note this comes with some pitfalls.
|
// unregister() to register() below. Note this comes with some pitfalls.
|
||||||
// Learn more about service workers: https://cra.link/PWA
|
// Learn more about service workers: https://cra.link/PWA
|
||||||
serviceWorkerRegistration.register();
|
registerServiceWorker({
|
||||||
|
onUpdate() {
|
||||||
|
store.dispatch(appUpdateAvailable()); // eslint-disable-line @typescript-eslint/no-unsafe-call
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
|
@ -17,6 +17,7 @@ import mercureInfoReducer from '../mercure/reducers/mercureInfo';
|
||||||
import settingsReducer from '../settings/reducers/settings';
|
import settingsReducer from '../settings/reducers/settings';
|
||||||
import domainsListReducer from '../domains/reducers/domainsList';
|
import domainsListReducer from '../domains/reducers/domainsList';
|
||||||
import visitsOverviewReducer from '../visits/reducers/visitsOverview';
|
import visitsOverviewReducer from '../visits/reducers/visitsOverview';
|
||||||
|
import appUpdatesReducer from '../app/reducers/appUpdates';
|
||||||
import { ShlinkState } from '../container/types';
|
import { ShlinkState } from '../container/types';
|
||||||
|
|
||||||
export default combineReducers<ShlinkState>({
|
export default combineReducers<ShlinkState>({
|
||||||
|
@ -38,4 +39,5 @@ export default combineReducers<ShlinkState>({
|
||||||
settings: settingsReducer,
|
settings: settingsReducer,
|
||||||
domainsList: domainsListReducer,
|
domainsList: domainsListReducer,
|
||||||
visitsOverview: visitsOverviewReducer,
|
visitsOverview: visitsOverviewReducer,
|
||||||
|
appUpdated: appUpdatesReducer,
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in a new issue