mirror of
https://github.com/shlinkio/shlink-web-client.git
synced 2025-01-09 17:57:26 +03:00
Added form controls to set real time updates interval
This commit is contained in:
parent
5d6d802d64
commit
9b45513684
5 changed files with 66 additions and 14 deletions
|
@ -1,20 +1,49 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Card, CardBody, CardHeader } from 'reactstrap';
|
import { Card, CardBody, CardHeader, FormGroup, Input } from 'reactstrap';
|
||||||
|
import classNames from 'classnames';
|
||||||
import ToggleSwitch from '../utils/ToggleSwitch';
|
import ToggleSwitch from '../utils/ToggleSwitch';
|
||||||
import { Settings } from './reducers/settings';
|
import { Settings } from './reducers/settings';
|
||||||
|
|
||||||
interface RealTimeUpdatesProps {
|
interface RealTimeUpdatesProps {
|
||||||
settings: Settings;
|
settings: Settings;
|
||||||
setRealTimeUpdates: (enabled: boolean) => void;
|
toggleRealTimeUpdates: (enabled: boolean) => void;
|
||||||
|
setRealTimeUpdatesInterval: (interval: number) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const RealTimeUpdates = ({ settings: { realTimeUpdates }, setRealTimeUpdates }: RealTimeUpdatesProps) => (
|
const intervalValue = (interval?: number) => !interval ? '' : `${interval}`;
|
||||||
|
|
||||||
|
const RealTimeUpdates = (
|
||||||
|
{ settings: { realTimeUpdates }, toggleRealTimeUpdates, setRealTimeUpdatesInterval }: RealTimeUpdatesProps,
|
||||||
|
) => (
|
||||||
<Card>
|
<Card>
|
||||||
<CardHeader>Real-time updates</CardHeader>
|
<CardHeader>Real-time updates</CardHeader>
|
||||||
<CardBody>
|
<CardBody>
|
||||||
<ToggleSwitch checked={realTimeUpdates.enabled} onChange={setRealTimeUpdates}>
|
<FormGroup>
|
||||||
|
<ToggleSwitch checked={realTimeUpdates.enabled} onChange={toggleRealTimeUpdates}>
|
||||||
Enable or disable real-time updates, when using Shlink v2.2.0 or newer.
|
Enable or disable real-time updates, when using Shlink v2.2.0 or newer.
|
||||||
</ToggleSwitch>
|
</ToggleSwitch>
|
||||||
|
</FormGroup>
|
||||||
|
<FormGroup className="mb-0">
|
||||||
|
<label className={classNames({ 'text-muted': !realTimeUpdates.enabled })}>
|
||||||
|
Real-time updates frequency (in minutes):
|
||||||
|
</label>
|
||||||
|
<Input
|
||||||
|
type="number"
|
||||||
|
min={0}
|
||||||
|
placeholder={realTimeUpdates.enabled ? 'Immediate' : ''}
|
||||||
|
disabled={!realTimeUpdates.enabled}
|
||||||
|
value={intervalValue(realTimeUpdates.interval)}
|
||||||
|
onChange={(e) => setRealTimeUpdatesInterval(Number(e.target.value))}
|
||||||
|
/>
|
||||||
|
{realTimeUpdates.enabled && (
|
||||||
|
<small className="form-text text-muted">
|
||||||
|
{realTimeUpdates.interval !== undefined && realTimeUpdates.interval > 0 &&
|
||||||
|
<span>Updates will be reflected in the UI every <b>{realTimeUpdates.interval}</b> minutes.</span>
|
||||||
|
}
|
||||||
|
{!realTimeUpdates.interval && 'Updates will be reflected in the UI as soon as they happen.'}
|
||||||
|
</small>
|
||||||
|
)}
|
||||||
|
</FormGroup>
|
||||||
</CardBody>
|
</CardBody>
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,10 +1,13 @@
|
||||||
import { Action } from 'redux';
|
import { Action } from 'redux';
|
||||||
|
import { mergeDeepRight } from 'ramda';
|
||||||
import { buildReducer } from '../../utils/helpers/redux';
|
import { buildReducer } from '../../utils/helpers/redux';
|
||||||
|
import { RecursivePartial } from '../../utils/utils';
|
||||||
|
|
||||||
export const SET_REAL_TIME_UPDATES = 'shlink/realTimeUpdates/SET_REAL_TIME_UPDATES';
|
export const SET_REAL_TIME_UPDATES = 'shlink/realTimeUpdates/SET_REAL_TIME_UPDATES';
|
||||||
|
|
||||||
interface RealTimeUpdates {
|
interface RealTimeUpdates {
|
||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
|
interval?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Settings {
|
export interface Settings {
|
||||||
|
@ -19,11 +22,18 @@ const initialState: Settings = {
|
||||||
|
|
||||||
type SettingsAction = Action & Settings;
|
type SettingsAction = Action & Settings;
|
||||||
|
|
||||||
|
type PartialSettingsAction = Action & RecursivePartial<Settings>;
|
||||||
|
|
||||||
export default buildReducer<Settings, SettingsAction>({
|
export default buildReducer<Settings, SettingsAction>({
|
||||||
[SET_REAL_TIME_UPDATES]: (state, { realTimeUpdates }) => ({ ...state, realTimeUpdates }),
|
[SET_REAL_TIME_UPDATES]: (state, { realTimeUpdates }) => mergeDeepRight(state, { realTimeUpdates }),
|
||||||
}, initialState);
|
}, initialState);
|
||||||
|
|
||||||
export const setRealTimeUpdates = (enabled: boolean): SettingsAction => ({
|
export const toggleRealTimeUpdates = (enabled: boolean): PartialSettingsAction => ({
|
||||||
type: SET_REAL_TIME_UPDATES,
|
type: SET_REAL_TIME_UPDATES,
|
||||||
realTimeUpdates: { enabled },
|
realTimeUpdates: { enabled },
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const setRealTimeUpdatesInterval = (interval: number): PartialSettingsAction => ({
|
||||||
|
type: SET_REAL_TIME_UPDATES,
|
||||||
|
realTimeUpdates: { interval },
|
||||||
|
});
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import Bottle from 'bottlejs';
|
import Bottle from 'bottlejs';
|
||||||
import RealTimeUpdates from '../RealTimeUpdates';
|
import RealTimeUpdates from '../RealTimeUpdates';
|
||||||
import Settings from '../Settings';
|
import Settings from '../Settings';
|
||||||
import { setRealTimeUpdates } from '../reducers/settings';
|
import { setRealTimeUpdatesInterval, toggleRealTimeUpdates } from '../reducers/settings';
|
||||||
import { ConnectDecorator } from '../../container/types';
|
import { ConnectDecorator } from '../../container/types';
|
||||||
import { withoutSelectedServer } from '../../servers/helpers/withoutSelectedServer';
|
import { withoutSelectedServer } from '../../servers/helpers/withoutSelectedServer';
|
||||||
|
|
||||||
|
@ -13,10 +13,11 @@ const provideServices = (bottle: Bottle, connect: ConnectDecorator) => {
|
||||||
|
|
||||||
// Services
|
// Services
|
||||||
bottle.serviceFactory('RealTimeUpdates', () => RealTimeUpdates);
|
bottle.serviceFactory('RealTimeUpdates', () => RealTimeUpdates);
|
||||||
bottle.decorator('RealTimeUpdates', connect([ 'settings' ], [ 'setRealTimeUpdates' ]));
|
bottle.decorator('RealTimeUpdates', connect([ 'settings' ], [ 'setRealTimeUpdatesInterval' ]));
|
||||||
|
|
||||||
// Actions
|
// Actions
|
||||||
bottle.serviceFactory('setRealTimeUpdates', () => setRealTimeUpdates);
|
bottle.serviceFactory('toggleRealTimeUpdates', () => toggleRealTimeUpdates);
|
||||||
|
bottle.serviceFactory('setRealTimeUpdatesInterval', () => setRealTimeUpdatesInterval);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default provideServices;
|
export default provideServices;
|
||||||
|
|
|
@ -39,3 +39,7 @@ export type Nullable<T> = {
|
||||||
type Optional<T> = T | null | undefined;
|
type Optional<T> = T | null | undefined;
|
||||||
|
|
||||||
export type OptionalString = Optional<string>;
|
export type OptionalString = Optional<string>;
|
||||||
|
|
||||||
|
export type RecursivePartial<T> = {
|
||||||
|
[P in keyof T]?: RecursivePartial<T[P]>;
|
||||||
|
};
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import reducer, { SET_REAL_TIME_UPDATES, setRealTimeUpdates } from '../../../src/settings/reducers/settings';
|
import reducer, { SET_REAL_TIME_UPDATES, toggleRealTimeUpdates, setRealTimeUpdatesInterval } from '../../../src/settings/reducers/settings';
|
||||||
|
|
||||||
describe('settingsReducer', () => {
|
describe('settingsReducer', () => {
|
||||||
const realTimeUpdates = { enabled: true };
|
const realTimeUpdates = { enabled: true };
|
||||||
|
@ -9,11 +9,19 @@ describe('settingsReducer', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('setRealTimeUpdates', () => {
|
describe('toggleRealTimeUpdates', () => {
|
||||||
it.each([[ true ], [ false ]])('updates settings with provided value and then loads updates again', (enabled) => {
|
it.each([[ true ], [ false ]])('updates settings with provided value and then loads updates again', (enabled) => {
|
||||||
const result = setRealTimeUpdates(enabled);
|
const result = toggleRealTimeUpdates(enabled);
|
||||||
|
|
||||||
expect(result).toEqual({ type: SET_REAL_TIME_UPDATES, realTimeUpdates: { enabled } });
|
expect(result).toEqual({ type: SET_REAL_TIME_UPDATES, realTimeUpdates: { enabled } });
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('setRealTimeUpdatesInterval', () => {
|
||||||
|
it.each([[ 0 ], [ 1 ], [ 2 ], [ 10 ]])('updates settings with provided value and then loads updates again', (interval) => {
|
||||||
|
const result = setRealTimeUpdatesInterval(interval);
|
||||||
|
|
||||||
|
expect(result).toEqual({ type: SET_REAL_TIME_UPDATES, realTimeUpdates: { interval } });
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in a new issue